Coverage for /usr/lib/python3/dist-packages/sympy/printing/latex.py: 18%

1878 statements  

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

1""" 

2A Printer which converts an expression into its LaTeX equivalent. 

3""" 

4from __future__ import annotations 

5from typing import Any, Callable, TYPE_CHECKING 

6 

7import itertools 

8 

9from sympy.core import Add, Float, Mod, Mul, Number, S, Symbol, Expr 

10from sympy.core.alphabets import greeks 

11from sympy.core.containers import Tuple 

12from sympy.core.function import Function, AppliedUndef, Derivative 

13from sympy.core.operations import AssocOp 

14from sympy.core.power import Pow 

15from sympy.core.sorting import default_sort_key 

16from sympy.core.sympify import SympifyError 

17from sympy.logic.boolalg import true, BooleanTrue, BooleanFalse 

18from sympy.tensor.array import NDimArray 

19 

20# sympy.printing imports 

21from sympy.printing.precedence import precedence_traditional 

22from sympy.printing.printer import Printer, print_function 

23from sympy.printing.conventions import split_super_sub, requires_partial 

24from sympy.printing.precedence import precedence, PRECEDENCE 

25 

26from mpmath.libmp.libmpf import prec_to_dps, to_str as mlib_to_str 

27 

28from sympy.utilities.iterables import has_variety, sift 

29 

30import re 

31 

32if TYPE_CHECKING: 

33 from sympy.vector.basisdependent import BasisDependent 

34 

35# Hand-picked functions which can be used directly in both LaTeX and MathJax 

36# Complete list at 

37# https://docs.mathjax.org/en/latest/tex.html#supported-latex-commands 

38# This variable only contains those functions which SymPy uses. 

39accepted_latex_functions = ['arcsin', 'arccos', 'arctan', 'sin', 'cos', 'tan', 

40 'sinh', 'cosh', 'tanh', 'sqrt', 'ln', 'log', 'sec', 

41 'csc', 'cot', 'coth', 're', 'im', 'frac', 'root', 

42 'arg', 

43 ] 

44 

45tex_greek_dictionary = { 

46 'Alpha': r'\mathrm{A}', 

47 'Beta': r'\mathrm{B}', 

48 'Gamma': r'\Gamma', 

49 'Delta': r'\Delta', 

50 'Epsilon': r'\mathrm{E}', 

51 'Zeta': r'\mathrm{Z}', 

52 'Eta': r'\mathrm{H}', 

53 'Theta': r'\Theta', 

54 'Iota': r'\mathrm{I}', 

55 'Kappa': r'\mathrm{K}', 

56 'Lambda': r'\Lambda', 

57 'Mu': r'\mathrm{M}', 

58 'Nu': r'\mathrm{N}', 

59 'Xi': r'\Xi', 

60 'omicron': 'o', 

61 'Omicron': r'\mathrm{O}', 

62 'Pi': r'\Pi', 

63 'Rho': r'\mathrm{P}', 

64 'Sigma': r'\Sigma', 

65 'Tau': r'\mathrm{T}', 

66 'Upsilon': r'\Upsilon', 

67 'Phi': r'\Phi', 

68 'Chi': r'\mathrm{X}', 

69 'Psi': r'\Psi', 

70 'Omega': r'\Omega', 

71 'lamda': r'\lambda', 

72 'Lamda': r'\Lambda', 

73 'khi': r'\chi', 

74 'Khi': r'\mathrm{X}', 

75 'varepsilon': r'\varepsilon', 

76 'varkappa': r'\varkappa', 

77 'varphi': r'\varphi', 

78 'varpi': r'\varpi', 

79 'varrho': r'\varrho', 

80 'varsigma': r'\varsigma', 

81 'vartheta': r'\vartheta', 

82} 

83 

84other_symbols = {'aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar', 

85 'hslash', 'mho', 'wp'} 

86 

87# Variable name modifiers 

88modifier_dict: dict[str, Callable[[str], str]] = { 

89 # Accents 

90 'mathring': lambda s: r'\mathring{'+s+r'}', 

91 'ddddot': lambda s: r'\ddddot{'+s+r'}', 

92 'dddot': lambda s: r'\dddot{'+s+r'}', 

93 'ddot': lambda s: r'\ddot{'+s+r'}', 

94 'dot': lambda s: r'\dot{'+s+r'}', 

95 'check': lambda s: r'\check{'+s+r'}', 

96 'breve': lambda s: r'\breve{'+s+r'}', 

97 'acute': lambda s: r'\acute{'+s+r'}', 

98 'grave': lambda s: r'\grave{'+s+r'}', 

99 'tilde': lambda s: r'\tilde{'+s+r'}', 

100 'hat': lambda s: r'\hat{'+s+r'}', 

101 'bar': lambda s: r'\bar{'+s+r'}', 

102 'vec': lambda s: r'\vec{'+s+r'}', 

103 'prime': lambda s: "{"+s+"}'", 

104 'prm': lambda s: "{"+s+"}'", 

105 # Faces 

106 'bold': lambda s: r'\boldsymbol{'+s+r'}', 

107 'bm': lambda s: r'\boldsymbol{'+s+r'}', 

108 'cal': lambda s: r'\mathcal{'+s+r'}', 

109 'scr': lambda s: r'\mathscr{'+s+r'}', 

110 'frak': lambda s: r'\mathfrak{'+s+r'}', 

111 # Brackets 

112 'norm': lambda s: r'\left\|{'+s+r'}\right\|', 

113 'avg': lambda s: r'\left\langle{'+s+r'}\right\rangle', 

114 'abs': lambda s: r'\left|{'+s+r'}\right|', 

115 'mag': lambda s: r'\left|{'+s+r'}\right|', 

116} 

117 

118greek_letters_set = frozenset(greeks) 

119 

120_between_two_numbers_p = ( 

121 re.compile(r'[0-9][} ]*$'), # search 

122 re.compile(r'[0-9]'), # match 

123) 

124 

125 

126def latex_escape(s: str) -> str: 

127 """ 

128 Escape a string such that latex interprets it as plaintext. 

129 

130 We cannot use verbatim easily with mathjax, so escaping is easier. 

131 Rules from https://tex.stackexchange.com/a/34586/41112. 

132 """ 

133 s = s.replace('\\', r'\textbackslash') 

134 for c in '&%$#_{}': 

135 s = s.replace(c, '\\' + c) 

136 s = s.replace('~', r'\textasciitilde') 

137 s = s.replace('^', r'\textasciicircum') 

138 return s 

139 

140 

141class LatexPrinter(Printer): 

142 printmethod = "_latex" 

143 

144 _default_settings: dict[str, Any] = { 

145 "full_prec": False, 

146 "fold_frac_powers": False, 

147 "fold_func_brackets": False, 

148 "fold_short_frac": None, 

149 "inv_trig_style": "abbreviated", 

150 "itex": False, 

151 "ln_notation": False, 

152 "long_frac_ratio": None, 

153 "mat_delim": "[", 

154 "mat_str": None, 

155 "mode": "plain", 

156 "mul_symbol": None, 

157 "order": None, 

158 "symbol_names": {}, 

159 "root_notation": True, 

160 "mat_symbol_style": "plain", 

161 "imaginary_unit": "i", 

162 "gothic_re_im": False, 

163 "decimal_separator": "period", 

164 "perm_cyclic": True, 

165 "parenthesize_super": True, 

166 "min": None, 

167 "max": None, 

168 "diff_operator": "d", 

169 } 

170 

171 def __init__(self, settings=None): 

172 Printer.__init__(self, settings) 

173 

174 if 'mode' in self._settings: 

175 valid_modes = ['inline', 'plain', 'equation', 

176 'equation*'] 

177 if self._settings['mode'] not in valid_modes: 

178 raise ValueError("'mode' must be one of 'inline', 'plain', " 

179 "'equation' or 'equation*'") 

180 

181 if self._settings['fold_short_frac'] is None and \ 

182 self._settings['mode'] == 'inline': 

183 self._settings['fold_short_frac'] = True 

184 

185 mul_symbol_table = { 

186 None: r" ", 

187 "ldot": r" \,.\, ", 

188 "dot": r" \cdot ", 

189 "times": r" \times " 

190 } 

191 try: 

192 self._settings['mul_symbol_latex'] = \ 

193 mul_symbol_table[self._settings['mul_symbol']] 

194 except KeyError: 

195 self._settings['mul_symbol_latex'] = \ 

196 self._settings['mul_symbol'] 

197 try: 

198 self._settings['mul_symbol_latex_numbers'] = \ 

199 mul_symbol_table[self._settings['mul_symbol'] or 'dot'] 

200 except KeyError: 

201 if (self._settings['mul_symbol'].strip() in 

202 ['', ' ', '\\', '\\,', '\\:', '\\;', '\\quad']): 

203 self._settings['mul_symbol_latex_numbers'] = \ 

204 mul_symbol_table['dot'] 

205 else: 

206 self._settings['mul_symbol_latex_numbers'] = \ 

207 self._settings['mul_symbol'] 

208 

209 self._delim_dict = {'(': ')', '[': ']'} 

210 

211 imaginary_unit_table = { 

212 None: r"i", 

213 "i": r"i", 

214 "ri": r"\mathrm{i}", 

215 "ti": r"\text{i}", 

216 "j": r"j", 

217 "rj": r"\mathrm{j}", 

218 "tj": r"\text{j}", 

219 } 

220 imag_unit = self._settings['imaginary_unit'] 

221 self._settings['imaginary_unit_latex'] = imaginary_unit_table.get(imag_unit, imag_unit) 

222 

223 diff_operator_table = { 

224 None: r"d", 

225 "d": r"d", 

226 "rd": r"\mathrm{d}", 

227 "td": r"\text{d}", 

228 } 

229 diff_operator = self._settings['diff_operator'] 

230 self._settings["diff_operator_latex"] = diff_operator_table.get(diff_operator, diff_operator) 

231 

232 def _add_parens(self, s) -> str: 

233 return r"\left({}\right)".format(s) 

234 

235 # TODO: merge this with the above, which requires a lot of test changes 

236 def _add_parens_lspace(self, s) -> str: 

237 return r"\left( {}\right)".format(s) 

238 

239 def parenthesize(self, item, level, is_neg=False, strict=False) -> str: 

240 prec_val = precedence_traditional(item) 

241 if is_neg and strict: 

242 return self._add_parens(self._print(item)) 

243 

244 if (prec_val < level) or ((not strict) and prec_val <= level): 

245 return self._add_parens(self._print(item)) 

246 else: 

247 return self._print(item) 

248 

249 def parenthesize_super(self, s): 

250 """ 

251 Protect superscripts in s 

252 

253 If the parenthesize_super option is set, protect with parentheses, else 

254 wrap in braces. 

255 """ 

256 if "^" in s: 

257 if self._settings['parenthesize_super']: 

258 return self._add_parens(s) 

259 else: 

260 return "{{{}}}".format(s) 

261 return s 

262 

263 def doprint(self, expr) -> str: 

264 tex = Printer.doprint(self, expr) 

265 

266 if self._settings['mode'] == 'plain': 

267 return tex 

268 elif self._settings['mode'] == 'inline': 

269 return r"$%s$" % tex 

270 elif self._settings['itex']: 

271 return r"$$%s$$" % tex 

272 else: 

273 env_str = self._settings['mode'] 

274 return r"\begin{%s}%s\end{%s}" % (env_str, tex, env_str) 

275 

276 def _needs_brackets(self, expr) -> bool: 

277 """ 

278 Returns True if the expression needs to be wrapped in brackets when 

279 printed, False otherwise. For example: a + b => True; a => False; 

280 10 => False; -10 => True. 

281 """ 

282 return not ((expr.is_Integer and expr.is_nonnegative) 

283 or (expr.is_Atom and (expr is not S.NegativeOne 

284 and expr.is_Rational is False))) 

285 

286 def _needs_function_brackets(self, expr) -> bool: 

287 """ 

288 Returns True if the expression needs to be wrapped in brackets when 

289 passed as an argument to a function, False otherwise. This is a more 

290 liberal version of _needs_brackets, in that many expressions which need 

291 to be wrapped in brackets when added/subtracted/raised to a power do 

292 not need them when passed to a function. Such an example is a*b. 

293 """ 

294 if not self._needs_brackets(expr): 

295 return False 

296 else: 

297 # Muls of the form a*b*c... can be folded 

298 if expr.is_Mul and not self._mul_is_clean(expr): 

299 return True 

300 # Pows which don't need brackets can be folded 

301 elif expr.is_Pow and not self._pow_is_clean(expr): 

302 return True 

303 # Add and Function always need brackets 

304 elif expr.is_Add or expr.is_Function: 

305 return True 

306 else: 

307 return False 

308 

309 def _needs_mul_brackets(self, expr, first=False, last=False) -> bool: 

310 """ 

311 Returns True if the expression needs to be wrapped in brackets when 

312 printed as part of a Mul, False otherwise. This is True for Add, 

313 but also for some container objects that would not need brackets 

314 when appearing last in a Mul, e.g. an Integral. ``last=True`` 

315 specifies that this expr is the last to appear in a Mul. 

316 ``first=True`` specifies that this expr is the first to appear in 

317 a Mul. 

318 """ 

319 from sympy.concrete.products import Product 

320 from sympy.concrete.summations import Sum 

321 from sympy.integrals.integrals import Integral 

322 

323 if expr.is_Mul: 

324 if not first and expr.could_extract_minus_sign(): 

325 return True 

326 elif precedence_traditional(expr) < PRECEDENCE["Mul"]: 

327 return True 

328 elif expr.is_Relational: 

329 return True 

330 if expr.is_Piecewise: 

331 return True 

332 if any(expr.has(x) for x in (Mod,)): 

333 return True 

334 if (not last and 

335 any(expr.has(x) for x in (Integral, Product, Sum))): 

336 return True 

337 

338 return False 

339 

340 def _needs_add_brackets(self, expr) -> bool: 

341 """ 

342 Returns True if the expression needs to be wrapped in brackets when 

343 printed as part of an Add, False otherwise. This is False for most 

344 things. 

345 """ 

346 if expr.is_Relational: 

347 return True 

348 if any(expr.has(x) for x in (Mod,)): 

349 return True 

350 if expr.is_Add: 

351 return True 

352 return False 

353 

354 def _mul_is_clean(self, expr) -> bool: 

355 for arg in expr.args: 

356 if arg.is_Function: 

357 return False 

358 return True 

359 

360 def _pow_is_clean(self, expr) -> bool: 

361 return not self._needs_brackets(expr.base) 

362 

363 def _do_exponent(self, expr: str, exp): 

364 if exp is not None: 

365 return r"\left(%s\right)^{%s}" % (expr, exp) 

366 else: 

367 return expr 

368 

369 def _print_Basic(self, expr): 

370 name = self._deal_with_super_sub(expr.__class__.__name__) 

371 if expr.args: 

372 ls = [self._print(o) for o in expr.args] 

373 s = r"\operatorname{{{}}}\left({}\right)" 

374 return s.format(name, ", ".join(ls)) 

375 else: 

376 return r"\text{{{}}}".format(name) 

377 

378 def _print_bool(self, e: bool | BooleanTrue | BooleanFalse): 

379 return r"\text{%s}" % e 

380 

381 _print_BooleanTrue = _print_bool 

382 _print_BooleanFalse = _print_bool 

383 

384 def _print_NoneType(self, e): 

385 return r"\text{%s}" % e 

386 

387 def _print_Add(self, expr, order=None): 

388 terms = self._as_ordered_terms(expr, order=order) 

389 

390 tex = "" 

391 for i, term in enumerate(terms): 

392 if i == 0: 

393 pass 

394 elif term.could_extract_minus_sign(): 

395 tex += " - " 

396 term = -term 

397 else: 

398 tex += " + " 

399 term_tex = self._print(term) 

400 if self._needs_add_brackets(term): 

401 term_tex = r"\left(%s\right)" % term_tex 

402 tex += term_tex 

403 

404 return tex 

405 

406 def _print_Cycle(self, expr): 

407 from sympy.combinatorics.permutations import Permutation 

408 if expr.size == 0: 

409 return r"\left( \right)" 

410 expr = Permutation(expr) 

411 expr_perm = expr.cyclic_form 

412 siz = expr.size 

413 if expr.array_form[-1] == siz - 1: 

414 expr_perm = expr_perm + [[siz - 1]] 

415 term_tex = '' 

416 for i in expr_perm: 

417 term_tex += str(i).replace(',', r"\;") 

418 term_tex = term_tex.replace('[', r"\left( ") 

419 term_tex = term_tex.replace(']', r"\right)") 

420 return term_tex 

421 

422 def _print_Permutation(self, expr): 

423 from sympy.combinatorics.permutations import Permutation 

424 from sympy.utilities.exceptions import sympy_deprecation_warning 

425 

426 perm_cyclic = Permutation.print_cyclic 

427 if perm_cyclic is not None: 

428 sympy_deprecation_warning( 

429 f""" 

430 Setting Permutation.print_cyclic is deprecated. Instead use 

431 init_printing(perm_cyclic={perm_cyclic}). 

432 """, 

433 deprecated_since_version="1.6", 

434 active_deprecations_target="deprecated-permutation-print_cyclic", 

435 stacklevel=8, 

436 ) 

437 else: 

438 perm_cyclic = self._settings.get("perm_cyclic", True) 

439 

440 if perm_cyclic: 

441 return self._print_Cycle(expr) 

442 

443 if expr.size == 0: 

444 return r"\left( \right)" 

445 

446 lower = [self._print(arg) for arg in expr.array_form] 

447 upper = [self._print(arg) for arg in range(len(lower))] 

448 

449 row1 = " & ".join(upper) 

450 row2 = " & ".join(lower) 

451 mat = r" \\ ".join((row1, row2)) 

452 return r"\begin{pmatrix} %s \end{pmatrix}" % mat 

453 

454 

455 def _print_AppliedPermutation(self, expr): 

456 perm, var = expr.args 

457 return r"\sigma_{%s}(%s)" % (self._print(perm), self._print(var)) 

458 

459 def _print_Float(self, expr): 

460 # Based off of that in StrPrinter 

461 dps = prec_to_dps(expr._prec) 

462 strip = False if self._settings['full_prec'] else True 

463 low = self._settings["min"] if "min" in self._settings else None 

464 high = self._settings["max"] if "max" in self._settings else None 

465 str_real = mlib_to_str(expr._mpf_, dps, strip_zeros=strip, min_fixed=low, max_fixed=high) 

466 

467 # Must always have a mul symbol (as 2.5 10^{20} just looks odd) 

468 # thus we use the number separator 

469 separator = self._settings['mul_symbol_latex_numbers'] 

470 

471 if 'e' in str_real: 

472 (mant, exp) = str_real.split('e') 

473 

474 if exp[0] == '+': 

475 exp = exp[1:] 

476 if self._settings['decimal_separator'] == 'comma': 

477 mant = mant.replace('.','{,}') 

478 

479 return r"%s%s10^{%s}" % (mant, separator, exp) 

480 elif str_real == "+inf": 

481 return r"\infty" 

482 elif str_real == "-inf": 

483 return r"- \infty" 

484 else: 

485 if self._settings['decimal_separator'] == 'comma': 

486 str_real = str_real.replace('.','{,}') 

487 return str_real 

488 

489 def _print_Cross(self, expr): 

490 vec1 = expr._expr1 

491 vec2 = expr._expr2 

492 return r"%s \times %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']), 

493 self.parenthesize(vec2, PRECEDENCE['Mul'])) 

494 

495 def _print_Curl(self, expr): 

496 vec = expr._expr 

497 return r"\nabla\times %s" % self.parenthesize(vec, PRECEDENCE['Mul']) 

498 

499 def _print_Divergence(self, expr): 

500 vec = expr._expr 

501 return r"\nabla\cdot %s" % self.parenthesize(vec, PRECEDENCE['Mul']) 

502 

503 def _print_Dot(self, expr): 

504 vec1 = expr._expr1 

505 vec2 = expr._expr2 

506 return r"%s \cdot %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']), 

507 self.parenthesize(vec2, PRECEDENCE['Mul'])) 

508 

509 def _print_Gradient(self, expr): 

510 func = expr._expr 

511 return r"\nabla %s" % self.parenthesize(func, PRECEDENCE['Mul']) 

512 

513 def _print_Laplacian(self, expr): 

514 func = expr._expr 

515 return r"\Delta %s" % self.parenthesize(func, PRECEDENCE['Mul']) 

516 

517 def _print_Mul(self, expr: Expr): 

518 from sympy.simplify import fraction 

519 separator: str = self._settings['mul_symbol_latex'] 

520 numbersep: str = self._settings['mul_symbol_latex_numbers'] 

521 

522 def convert(expr) -> str: 

523 if not expr.is_Mul: 

524 return str(self._print(expr)) 

525 else: 

526 if self.order not in ('old', 'none'): 

527 args = expr.as_ordered_factors() 

528 else: 

529 args = list(expr.args) 

530 

531 # If there are quantities or prefixes, append them at the back. 

532 units, nonunits = sift(args, lambda x: (hasattr(x, "_scale_factor") or hasattr(x, "is_physical_constant")) or 

533 (isinstance(x, Pow) and 

534 hasattr(x.base, "is_physical_constant")), binary=True) 

535 prefixes, units = sift(units, lambda x: hasattr(x, "_scale_factor"), binary=True) 

536 return convert_args(nonunits + prefixes + units) 

537 

538 def convert_args(args) -> str: 

539 _tex = last_term_tex = "" 

540 

541 for i, term in enumerate(args): 

542 term_tex = self._print(term) 

543 if not (hasattr(term, "_scale_factor") or hasattr(term, "is_physical_constant")): 

544 if self._needs_mul_brackets(term, first=(i == 0), 

545 last=(i == len(args) - 1)): 

546 term_tex = r"\left(%s\right)" % term_tex 

547 

548 if _between_two_numbers_p[0].search(last_term_tex) and \ 

549 _between_two_numbers_p[1].match(str(term)): 

550 # between two numbers 

551 _tex += numbersep 

552 elif _tex: 

553 _tex += separator 

554 elif _tex: 

555 _tex += separator 

556 

557 _tex += term_tex 

558 last_term_tex = term_tex 

559 return _tex 

560 

561 # Check for unevaluated Mul. In this case we need to make sure the 

562 # identities are visible, multiple Rational factors are not combined 

563 # etc so we display in a straight-forward form that fully preserves all 

564 # args and their order. 

565 # XXX: _print_Pow calls this routine with instances of Pow... 

566 if isinstance(expr, Mul): 

567 args = expr.args 

568 if args[0] is S.One or any(isinstance(arg, Number) for arg in args[1:]): 

569 return convert_args(args) 

570 

571 include_parens = False 

572 if expr.could_extract_minus_sign(): 

573 expr = -expr 

574 tex = "- " 

575 if expr.is_Add: 

576 tex += "(" 

577 include_parens = True 

578 else: 

579 tex = "" 

580 

581 numer, denom = fraction(expr, exact=True) 

582 

583 if denom is S.One and Pow(1, -1, evaluate=False) not in expr.args: 

584 # use the original expression here, since fraction() may have 

585 # altered it when producing numer and denom 

586 tex += convert(expr) 

587 

588 else: 

589 snumer = convert(numer) 

590 sdenom = convert(denom) 

591 ldenom = len(sdenom.split()) 

592 ratio = self._settings['long_frac_ratio'] 

593 if self._settings['fold_short_frac'] and ldenom <= 2 and \ 

594 "^" not in sdenom: 

595 # handle short fractions 

596 if self._needs_mul_brackets(numer, last=False): 

597 tex += r"\left(%s\right) / %s" % (snumer, sdenom) 

598 else: 

599 tex += r"%s / %s" % (snumer, sdenom) 

600 elif ratio is not None and \ 

601 len(snumer.split()) > ratio*ldenom: 

602 # handle long fractions 

603 if self._needs_mul_brackets(numer, last=True): 

604 tex += r"\frac{1}{%s}%s\left(%s\right)" \ 

605 % (sdenom, separator, snumer) 

606 elif numer.is_Mul: 

607 # split a long numerator 

608 a = S.One 

609 b = S.One 

610 for x in numer.args: 

611 if self._needs_mul_brackets(x, last=False) or \ 

612 len(convert(a*x).split()) > ratio*ldenom or \ 

613 (b.is_commutative is x.is_commutative is False): 

614 b *= x 

615 else: 

616 a *= x 

617 if self._needs_mul_brackets(b, last=True): 

618 tex += r"\frac{%s}{%s}%s\left(%s\right)" \ 

619 % (convert(a), sdenom, separator, convert(b)) 

620 else: 

621 tex += r"\frac{%s}{%s}%s%s" \ 

622 % (convert(a), sdenom, separator, convert(b)) 

623 else: 

624 tex += r"\frac{1}{%s}%s%s" % (sdenom, separator, snumer) 

625 else: 

626 tex += r"\frac{%s}{%s}" % (snumer, sdenom) 

627 

628 if include_parens: 

629 tex += ")" 

630 return tex 

631 

632 def _print_AlgebraicNumber(self, expr): 

633 if expr.is_aliased: 

634 return self._print(expr.as_poly().as_expr()) 

635 else: 

636 return self._print(expr.as_expr()) 

637 

638 def _print_PrimeIdeal(self, expr): 

639 p = self._print(expr.p) 

640 if expr.is_inert: 

641 return rf'\left({p}\right)' 

642 alpha = self._print(expr.alpha.as_expr()) 

643 return rf'\left({p}, {alpha}\right)' 

644 

645 def _print_Pow(self, expr: Pow): 

646 # Treat x**Rational(1,n) as special case 

647 if expr.exp.is_Rational: 

648 p: int = expr.exp.p # type: ignore 

649 q: int = expr.exp.q # type: ignore 

650 if abs(p) == 1 and q != 1 and self._settings['root_notation']: 

651 base = self._print(expr.base) 

652 if q == 2: 

653 tex = r"\sqrt{%s}" % base 

654 elif self._settings['itex']: 

655 tex = r"\root{%d}{%s}" % (q, base) 

656 else: 

657 tex = r"\sqrt[%d]{%s}" % (q, base) 

658 if expr.exp.is_negative: 

659 return r"\frac{1}{%s}" % tex 

660 else: 

661 return tex 

662 elif self._settings['fold_frac_powers'] and q != 1: 

663 base = self.parenthesize(expr.base, PRECEDENCE['Pow']) 

664 # issue #12886: add parentheses for superscripts raised to powers 

665 if expr.base.is_Symbol: 

666 base = self.parenthesize_super(base) 

667 if expr.base.is_Function: 

668 return self._print(expr.base, exp="%s/%s" % (p, q)) 

669 return r"%s^{%s/%s}" % (base, p, q) 

670 elif expr.exp.is_negative and expr.base.is_commutative: 

671 # special case for 1^(-x), issue 9216 

672 if expr.base == 1: 

673 return r"%s^{%s}" % (expr.base, expr.exp) 

674 # special case for (1/x)^(-y) and (-1/-x)^(-y), issue 20252 

675 if expr.base.is_Rational: 

676 base_p: int = expr.base.p # type: ignore 

677 base_q: int = expr.base.q # type: ignore 

678 if base_p * base_q == abs(base_q): 

679 if expr.exp == -1: 

680 return r"\frac{1}{\frac{%s}{%s}}" % (base_p, base_q) 

681 else: 

682 return r"\frac{1}{(\frac{%s}{%s})^{%s}}" % (base_p, base_q, abs(expr.exp)) 

683 # things like 1/x 

684 return self._print_Mul(expr) 

685 if expr.base.is_Function: 

686 return self._print(expr.base, exp=self._print(expr.exp)) 

687 tex = r"%s^{%s}" 

688 return self._helper_print_standard_power(expr, tex) 

689 

690 def _helper_print_standard_power(self, expr, template: str) -> str: 

691 exp = self._print(expr.exp) 

692 # issue #12886: add parentheses around superscripts raised 

693 # to powers 

694 base = self.parenthesize(expr.base, PRECEDENCE['Pow']) 

695 if expr.base.is_Symbol: 

696 base = self.parenthesize_super(base) 

697 elif (isinstance(expr.base, Derivative) 

698 and base.startswith(r'\left(') 

699 and re.match(r'\\left\(\\d?d?dot', base) 

700 and base.endswith(r'\right)')): 

701 # don't use parentheses around dotted derivative 

702 base = base[6: -7] # remove outermost added parens 

703 return template % (base, exp) 

704 

705 def _print_UnevaluatedExpr(self, expr): 

706 return self._print(expr.args[0]) 

707 

708 def _print_Sum(self, expr): 

709 if len(expr.limits) == 1: 

710 tex = r"\sum_{%s=%s}^{%s} " % \ 

711 tuple([self._print(i) for i in expr.limits[0]]) 

712 else: 

713 def _format_ineq(l): 

714 return r"%s \leq %s \leq %s" % \ 

715 tuple([self._print(s) for s in (l[1], l[0], l[2])]) 

716 

717 tex = r"\sum_{\substack{%s}} " % \ 

718 str.join('\\\\', [_format_ineq(l) for l in expr.limits]) 

719 

720 if isinstance(expr.function, Add): 

721 tex += r"\left(%s\right)" % self._print(expr.function) 

722 else: 

723 tex += self._print(expr.function) 

724 

725 return tex 

726 

727 def _print_Product(self, expr): 

728 if len(expr.limits) == 1: 

729 tex = r"\prod_{%s=%s}^{%s} " % \ 

730 tuple([self._print(i) for i in expr.limits[0]]) 

731 else: 

732 def _format_ineq(l): 

733 return r"%s \leq %s \leq %s" % \ 

734 tuple([self._print(s) for s in (l[1], l[0], l[2])]) 

735 

736 tex = r"\prod_{\substack{%s}} " % \ 

737 str.join('\\\\', [_format_ineq(l) for l in expr.limits]) 

738 

739 if isinstance(expr.function, Add): 

740 tex += r"\left(%s\right)" % self._print(expr.function) 

741 else: 

742 tex += self._print(expr.function) 

743 

744 return tex 

745 

746 def _print_BasisDependent(self, expr: 'BasisDependent'): 

747 from sympy.vector import Vector 

748 

749 o1: list[str] = [] 

750 if expr == expr.zero: 

751 return expr.zero._latex_form 

752 if isinstance(expr, Vector): 

753 items = expr.separate().items() 

754 else: 

755 items = [(0, expr)] 

756 

757 for system, vect in items: 

758 inneritems = list(vect.components.items()) 

759 inneritems.sort(key=lambda x: x[0].__str__()) 

760 for k, v in inneritems: 

761 if v == 1: 

762 o1.append(' + ' + k._latex_form) 

763 elif v == -1: 

764 o1.append(' - ' + k._latex_form) 

765 else: 

766 arg_str = r'\left(' + self._print(v) + r'\right)' 

767 o1.append(' + ' + arg_str + k._latex_form) 

768 

769 outstr = (''.join(o1)) 

770 if outstr[1] != '-': 

771 outstr = outstr[3:] 

772 else: 

773 outstr = outstr[1:] 

774 return outstr 

775 

776 def _print_Indexed(self, expr): 

777 tex_base = self._print(expr.base) 

778 tex = '{'+tex_base+'}'+'_{%s}' % ','.join( 

779 map(self._print, expr.indices)) 

780 return tex 

781 

782 def _print_IndexedBase(self, expr): 

783 return self._print(expr.label) 

784 

785 def _print_Idx(self, expr): 

786 label = self._print(expr.label) 

787 if expr.upper is not None: 

788 upper = self._print(expr.upper) 

789 if expr.lower is not None: 

790 lower = self._print(expr.lower) 

791 else: 

792 lower = self._print(S.Zero) 

793 interval = '{lower}\\mathrel{{..}}\\nobreak {upper}'.format( 

794 lower = lower, upper = upper) 

795 return '{{{label}}}_{{{interval}}}'.format( 

796 label = label, interval = interval) 

797 #if no bounds are defined this just prints the label 

798 return label 

799 

800 def _print_Derivative(self, expr): 

801 if requires_partial(expr.expr): 

802 diff_symbol = r'\partial' 

803 else: 

804 diff_symbol = self._settings["diff_operator_latex"] 

805 

806 tex = "" 

807 dim = 0 

808 for x, num in reversed(expr.variable_count): 

809 dim += num 

810 if num == 1: 

811 tex += r"%s %s" % (diff_symbol, self._print(x)) 

812 else: 

813 tex += r"%s %s^{%s}" % (diff_symbol, 

814 self.parenthesize_super(self._print(x)), 

815 self._print(num)) 

816 

817 if dim == 1: 

818 tex = r"\frac{%s}{%s}" % (diff_symbol, tex) 

819 else: 

820 tex = r"\frac{%s^{%s}}{%s}" % (diff_symbol, self._print(dim), tex) 

821 

822 if any(i.could_extract_minus_sign() for i in expr.args): 

823 return r"%s %s" % (tex, self.parenthesize(expr.expr, 

824 PRECEDENCE["Mul"], 

825 is_neg=True, 

826 strict=True)) 

827 

828 return r"%s %s" % (tex, self.parenthesize(expr.expr, 

829 PRECEDENCE["Mul"], 

830 is_neg=False, 

831 strict=True)) 

832 

833 def _print_Subs(self, subs): 

834 expr, old, new = subs.args 

835 latex_expr = self._print(expr) 

836 latex_old = (self._print(e) for e in old) 

837 latex_new = (self._print(e) for e in new) 

838 latex_subs = r'\\ '.join( 

839 e[0] + '=' + e[1] for e in zip(latex_old, latex_new)) 

840 return r'\left. %s \right|_{\substack{ %s }}' % (latex_expr, 

841 latex_subs) 

842 

843 def _print_Integral(self, expr): 

844 tex, symbols = "", [] 

845 diff_symbol = self._settings["diff_operator_latex"] 

846 

847 # Only up to \iiiint exists 

848 if len(expr.limits) <= 4 and all(len(lim) == 1 for lim in expr.limits): 

849 # Use len(expr.limits)-1 so that syntax highlighters don't think 

850 # \" is an escaped quote 

851 tex = r"\i" + "i"*(len(expr.limits) - 1) + "nt" 

852 symbols = [r"\, %s%s" % (diff_symbol, self._print(symbol[0])) 

853 for symbol in expr.limits] 

854 

855 else: 

856 for lim in reversed(expr.limits): 

857 symbol = lim[0] 

858 tex += r"\int" 

859 

860 if len(lim) > 1: 

861 if self._settings['mode'] != 'inline' \ 

862 and not self._settings['itex']: 

863 tex += r"\limits" 

864 

865 if len(lim) == 3: 

866 tex += "_{%s}^{%s}" % (self._print(lim[1]), 

867 self._print(lim[2])) 

868 if len(lim) == 2: 

869 tex += "^{%s}" % (self._print(lim[1])) 

870 

871 symbols.insert(0, r"\, %s%s" % (diff_symbol, self._print(symbol))) 

872 

873 return r"%s %s%s" % (tex, self.parenthesize(expr.function, 

874 PRECEDENCE["Mul"], 

875 is_neg=any(i.could_extract_minus_sign() for i in expr.args), 

876 strict=True), 

877 "".join(symbols)) 

878 

879 def _print_Limit(self, expr): 

880 e, z, z0, dir = expr.args 

881 

882 tex = r"\lim_{%s \to " % self._print(z) 

883 if str(dir) == '+-' or z0 in (S.Infinity, S.NegativeInfinity): 

884 tex += r"%s}" % self._print(z0) 

885 else: 

886 tex += r"%s^%s}" % (self._print(z0), self._print(dir)) 

887 

888 if isinstance(e, AssocOp): 

889 return r"%s\left(%s\right)" % (tex, self._print(e)) 

890 else: 

891 return r"%s %s" % (tex, self._print(e)) 

892 

893 def _hprint_Function(self, func: str) -> str: 

894 r''' 

895 Logic to decide how to render a function to latex 

896 - if it is a recognized latex name, use the appropriate latex command 

897 - if it is a single letter, excluding sub- and superscripts, just use that letter 

898 - if it is a longer name, then put \operatorname{} around it and be 

899 mindful of undercores in the name 

900 ''' 

901 func = self._deal_with_super_sub(func) 

902 superscriptidx = func.find("^") 

903 subscriptidx = func.find("_") 

904 if func in accepted_latex_functions: 

905 name = r"\%s" % func 

906 elif len(func) == 1 or func.startswith('\\') or subscriptidx == 1 or superscriptidx == 1: 

907 name = func 

908 else: 

909 if superscriptidx > 0 and subscriptidx > 0: 

910 name = r"\operatorname{%s}%s" %( 

911 func[:min(subscriptidx,superscriptidx)], 

912 func[min(subscriptidx,superscriptidx):]) 

913 elif superscriptidx > 0: 

914 name = r"\operatorname{%s}%s" %( 

915 func[:superscriptidx], 

916 func[superscriptidx:]) 

917 elif subscriptidx > 0: 

918 name = r"\operatorname{%s}%s" %( 

919 func[:subscriptidx], 

920 func[subscriptidx:]) 

921 else: 

922 name = r"\operatorname{%s}" % func 

923 return name 

924 

925 def _print_Function(self, expr: Function, exp=None) -> str: 

926 r''' 

927 Render functions to LaTeX, handling functions that LaTeX knows about 

928 e.g., sin, cos, ... by using the proper LaTeX command (\sin, \cos, ...). 

929 For single-letter function names, render them as regular LaTeX math 

930 symbols. For multi-letter function names that LaTeX does not know 

931 about, (e.g., Li, sech) use \operatorname{} so that the function name 

932 is rendered in Roman font and LaTeX handles spacing properly. 

933 

934 expr is the expression involving the function 

935 exp is an exponent 

936 ''' 

937 func = expr.func.__name__ 

938 if hasattr(self, '_print_' + func) and \ 

939 not isinstance(expr, AppliedUndef): 

940 return getattr(self, '_print_' + func)(expr, exp) 

941 else: 

942 args = [str(self._print(arg)) for arg in expr.args] 

943 # How inverse trig functions should be displayed, formats are: 

944 # abbreviated: asin, full: arcsin, power: sin^-1 

945 inv_trig_style = self._settings['inv_trig_style'] 

946 # If we are dealing with a power-style inverse trig function 

947 inv_trig_power_case = False 

948 # If it is applicable to fold the argument brackets 

949 can_fold_brackets = self._settings['fold_func_brackets'] and \ 

950 len(args) == 1 and \ 

951 not self._needs_function_brackets(expr.args[0]) 

952 

953 inv_trig_table = [ 

954 "asin", "acos", "atan", 

955 "acsc", "asec", "acot", 

956 "asinh", "acosh", "atanh", 

957 "acsch", "asech", "acoth", 

958 ] 

959 

960 # If the function is an inverse trig function, handle the style 

961 if func in inv_trig_table: 

962 if inv_trig_style == "abbreviated": 

963 pass 

964 elif inv_trig_style == "full": 

965 func = ("ar" if func[-1] == "h" else "arc") + func[1:] 

966 elif inv_trig_style == "power": 

967 func = func[1:] 

968 inv_trig_power_case = True 

969 

970 # Can never fold brackets if we're raised to a power 

971 if exp is not None: 

972 can_fold_brackets = False 

973 

974 if inv_trig_power_case: 

975 if func in accepted_latex_functions: 

976 name = r"\%s^{-1}" % func 

977 else: 

978 name = r"\operatorname{%s}^{-1}" % func 

979 elif exp is not None: 

980 func_tex = self._hprint_Function(func) 

981 func_tex = self.parenthesize_super(func_tex) 

982 name = r'%s^{%s}' % (func_tex, exp) 

983 else: 

984 name = self._hprint_Function(func) 

985 

986 if can_fold_brackets: 

987 if func in accepted_latex_functions: 

988 # Wrap argument safely to avoid parse-time conflicts 

989 # with the function name itself 

990 name += r" {%s}" 

991 else: 

992 name += r"%s" 

993 else: 

994 name += r"{\left(%s \right)}" 

995 

996 if inv_trig_power_case and exp is not None: 

997 name += r"^{%s}" % exp 

998 

999 return name % ",".join(args) 

1000 

1001 def _print_UndefinedFunction(self, expr): 

1002 return self._hprint_Function(str(expr)) 

1003 

1004 def _print_ElementwiseApplyFunction(self, expr): 

1005 return r"{%s}_{\circ}\left({%s}\right)" % ( 

1006 self._print(expr.function), 

1007 self._print(expr.expr), 

1008 ) 

1009 

1010 @property 

1011 def _special_function_classes(self): 

1012 from sympy.functions.special.tensor_functions import KroneckerDelta 

1013 from sympy.functions.special.gamma_functions import gamma, lowergamma 

1014 from sympy.functions.special.beta_functions import beta 

1015 from sympy.functions.special.delta_functions import DiracDelta 

1016 from sympy.functions.special.error_functions import Chi 

1017 return {KroneckerDelta: r'\delta', 

1018 gamma: r'\Gamma', 

1019 lowergamma: r'\gamma', 

1020 beta: r'\operatorname{B}', 

1021 DiracDelta: r'\delta', 

1022 Chi: r'\operatorname{Chi}'} 

1023 

1024 def _print_FunctionClass(self, expr): 

1025 for cls in self._special_function_classes: 

1026 if issubclass(expr, cls) and expr.__name__ == cls.__name__: 

1027 return self._special_function_classes[cls] 

1028 return self._hprint_Function(str(expr)) 

1029 

1030 def _print_Lambda(self, expr): 

1031 symbols, expr = expr.args 

1032 

1033 if len(symbols) == 1: 

1034 symbols = self._print(symbols[0]) 

1035 else: 

1036 symbols = self._print(tuple(symbols)) 

1037 

1038 tex = r"\left( %s \mapsto %s \right)" % (symbols, self._print(expr)) 

1039 

1040 return tex 

1041 

1042 def _print_IdentityFunction(self, expr): 

1043 return r"\left( x \mapsto x \right)" 

1044 

1045 def _hprint_variadic_function(self, expr, exp=None) -> str: 

1046 args = sorted(expr.args, key=default_sort_key) 

1047 texargs = [r"%s" % self._print(symbol) for symbol in args] 

1048 tex = r"\%s\left(%s\right)" % (str(expr.func).lower(), 

1049 ", ".join(texargs)) 

1050 if exp is not None: 

1051 return r"%s^{%s}" % (tex, exp) 

1052 else: 

1053 return tex 

1054 

1055 _print_Min = _print_Max = _hprint_variadic_function 

1056 

1057 def _print_floor(self, expr, exp=None): 

1058 tex = r"\left\lfloor{%s}\right\rfloor" % self._print(expr.args[0]) 

1059 

1060 if exp is not None: 

1061 return r"%s^{%s}" % (tex, exp) 

1062 else: 

1063 return tex 

1064 

1065 def _print_ceiling(self, expr, exp=None): 

1066 tex = r"\left\lceil{%s}\right\rceil" % self._print(expr.args[0]) 

1067 

1068 if exp is not None: 

1069 return r"%s^{%s}" % (tex, exp) 

1070 else: 

1071 return tex 

1072 

1073 def _print_log(self, expr, exp=None): 

1074 if not self._settings["ln_notation"]: 

1075 tex = r"\log{\left(%s \right)}" % self._print(expr.args[0]) 

1076 else: 

1077 tex = r"\ln{\left(%s \right)}" % self._print(expr.args[0]) 

1078 

1079 if exp is not None: 

1080 return r"%s^{%s}" % (tex, exp) 

1081 else: 

1082 return tex 

1083 

1084 def _print_Abs(self, expr, exp=None): 

1085 tex = r"\left|{%s}\right|" % self._print(expr.args[0]) 

1086 

1087 if exp is not None: 

1088 return r"%s^{%s}" % (tex, exp) 

1089 else: 

1090 return tex 

1091 

1092 def _print_re(self, expr, exp=None): 

1093 if self._settings['gothic_re_im']: 

1094 tex = r"\Re{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom']) 

1095 else: 

1096 tex = r"\operatorname{{re}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom'])) 

1097 

1098 return self._do_exponent(tex, exp) 

1099 

1100 def _print_im(self, expr, exp=None): 

1101 if self._settings['gothic_re_im']: 

1102 tex = r"\Im{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom']) 

1103 else: 

1104 tex = r"\operatorname{{im}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom'])) 

1105 

1106 return self._do_exponent(tex, exp) 

1107 

1108 def _print_Not(self, e): 

1109 from sympy.logic.boolalg import (Equivalent, Implies) 

1110 if isinstance(e.args[0], Equivalent): 

1111 return self._print_Equivalent(e.args[0], r"\not\Leftrightarrow") 

1112 if isinstance(e.args[0], Implies): 

1113 return self._print_Implies(e.args[0], r"\not\Rightarrow") 

1114 if (e.args[0].is_Boolean): 

1115 return r"\neg \left(%s\right)" % self._print(e.args[0]) 

1116 else: 

1117 return r"\neg %s" % self._print(e.args[0]) 

1118 

1119 def _print_LogOp(self, args, char): 

1120 arg = args[0] 

1121 if arg.is_Boolean and not arg.is_Not: 

1122 tex = r"\left(%s\right)" % self._print(arg) 

1123 else: 

1124 tex = r"%s" % self._print(arg) 

1125 

1126 for arg in args[1:]: 

1127 if arg.is_Boolean and not arg.is_Not: 

1128 tex += r" %s \left(%s\right)" % (char, self._print(arg)) 

1129 else: 

1130 tex += r" %s %s" % (char, self._print(arg)) 

1131 

1132 return tex 

1133 

1134 def _print_And(self, e): 

1135 args = sorted(e.args, key=default_sort_key) 

1136 return self._print_LogOp(args, r"\wedge") 

1137 

1138 def _print_Or(self, e): 

1139 args = sorted(e.args, key=default_sort_key) 

1140 return self._print_LogOp(args, r"\vee") 

1141 

1142 def _print_Xor(self, e): 

1143 args = sorted(e.args, key=default_sort_key) 

1144 return self._print_LogOp(args, r"\veebar") 

1145 

1146 def _print_Implies(self, e, altchar=None): 

1147 return self._print_LogOp(e.args, altchar or r"\Rightarrow") 

1148 

1149 def _print_Equivalent(self, e, altchar=None): 

1150 args = sorted(e.args, key=default_sort_key) 

1151 return self._print_LogOp(args, altchar or r"\Leftrightarrow") 

1152 

1153 def _print_conjugate(self, expr, exp=None): 

1154 tex = r"\overline{%s}" % self._print(expr.args[0]) 

1155 

1156 if exp is not None: 

1157 return r"%s^{%s}" % (tex, exp) 

1158 else: 

1159 return tex 

1160 

1161 def _print_polar_lift(self, expr, exp=None): 

1162 func = r"\operatorname{polar\_lift}" 

1163 arg = r"{\left(%s \right)}" % self._print(expr.args[0]) 

1164 

1165 if exp is not None: 

1166 return r"%s^{%s}%s" % (func, exp, arg) 

1167 else: 

1168 return r"%s%s" % (func, arg) 

1169 

1170 def _print_ExpBase(self, expr, exp=None): 

1171 # TODO should exp_polar be printed differently? 

1172 # what about exp_polar(0), exp_polar(1)? 

1173 tex = r"e^{%s}" % self._print(expr.args[0]) 

1174 return self._do_exponent(tex, exp) 

1175 

1176 def _print_Exp1(self, expr, exp=None): 

1177 return "e" 

1178 

1179 def _print_elliptic_k(self, expr, exp=None): 

1180 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1181 if exp is not None: 

1182 return r"K^{%s}%s" % (exp, tex) 

1183 else: 

1184 return r"K%s" % tex 

1185 

1186 def _print_elliptic_f(self, expr, exp=None): 

1187 tex = r"\left(%s\middle| %s\right)" % \ 

1188 (self._print(expr.args[0]), self._print(expr.args[1])) 

1189 if exp is not None: 

1190 return r"F^{%s}%s" % (exp, tex) 

1191 else: 

1192 return r"F%s" % tex 

1193 

1194 def _print_elliptic_e(self, expr, exp=None): 

1195 if len(expr.args) == 2: 

1196 tex = r"\left(%s\middle| %s\right)" % \ 

1197 (self._print(expr.args[0]), self._print(expr.args[1])) 

1198 else: 

1199 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1200 if exp is not None: 

1201 return r"E^{%s}%s" % (exp, tex) 

1202 else: 

1203 return r"E%s" % tex 

1204 

1205 def _print_elliptic_pi(self, expr, exp=None): 

1206 if len(expr.args) == 3: 

1207 tex = r"\left(%s; %s\middle| %s\right)" % \ 

1208 (self._print(expr.args[0]), self._print(expr.args[1]), 

1209 self._print(expr.args[2])) 

1210 else: 

1211 tex = r"\left(%s\middle| %s\right)" % \ 

1212 (self._print(expr.args[0]), self._print(expr.args[1])) 

1213 if exp is not None: 

1214 return r"\Pi^{%s}%s" % (exp, tex) 

1215 else: 

1216 return r"\Pi%s" % tex 

1217 

1218 def _print_beta(self, expr, exp=None): 

1219 x = expr.args[0] 

1220 # Deal with unevaluated single argument beta 

1221 y = expr.args[0] if len(expr.args) == 1 else expr.args[1] 

1222 tex = rf"\left({x}, {y}\right)" 

1223 

1224 if exp is not None: 

1225 return r"\operatorname{B}^{%s}%s" % (exp, tex) 

1226 else: 

1227 return r"\operatorname{B}%s" % tex 

1228 

1229 def _print_betainc(self, expr, exp=None, operator='B'): 

1230 largs = [self._print(arg) for arg in expr.args] 

1231 tex = r"\left(%s, %s\right)" % (largs[0], largs[1]) 

1232 

1233 if exp is not None: 

1234 return r"\operatorname{%s}_{(%s, %s)}^{%s}%s" % (operator, largs[2], largs[3], exp, tex) 

1235 else: 

1236 return r"\operatorname{%s}_{(%s, %s)}%s" % (operator, largs[2], largs[3], tex) 

1237 

1238 def _print_betainc_regularized(self, expr, exp=None): 

1239 return self._print_betainc(expr, exp, operator='I') 

1240 

1241 def _print_uppergamma(self, expr, exp=None): 

1242 tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), 

1243 self._print(expr.args[1])) 

1244 

1245 if exp is not None: 

1246 return r"\Gamma^{%s}%s" % (exp, tex) 

1247 else: 

1248 return r"\Gamma%s" % tex 

1249 

1250 def _print_lowergamma(self, expr, exp=None): 

1251 tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]), 

1252 self._print(expr.args[1])) 

1253 

1254 if exp is not None: 

1255 return r"\gamma^{%s}%s" % (exp, tex) 

1256 else: 

1257 return r"\gamma%s" % tex 

1258 

1259 def _hprint_one_arg_func(self, expr, exp=None) -> str: 

1260 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1261 

1262 if exp is not None: 

1263 return r"%s^{%s}%s" % (self._print(expr.func), exp, tex) 

1264 else: 

1265 return r"%s%s" % (self._print(expr.func), tex) 

1266 

1267 _print_gamma = _hprint_one_arg_func 

1268 

1269 def _print_Chi(self, expr, exp=None): 

1270 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1271 

1272 if exp is not None: 

1273 return r"\operatorname{Chi}^{%s}%s" % (exp, tex) 

1274 else: 

1275 return r"\operatorname{Chi}%s" % tex 

1276 

1277 def _print_expint(self, expr, exp=None): 

1278 tex = r"\left(%s\right)" % self._print(expr.args[1]) 

1279 nu = self._print(expr.args[0]) 

1280 

1281 if exp is not None: 

1282 return r"\operatorname{E}_{%s}^{%s}%s" % (nu, exp, tex) 

1283 else: 

1284 return r"\operatorname{E}_{%s}%s" % (nu, tex) 

1285 

1286 def _print_fresnels(self, expr, exp=None): 

1287 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1288 

1289 if exp is not None: 

1290 return r"S^{%s}%s" % (exp, tex) 

1291 else: 

1292 return r"S%s" % tex 

1293 

1294 def _print_fresnelc(self, expr, exp=None): 

1295 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1296 

1297 if exp is not None: 

1298 return r"C^{%s}%s" % (exp, tex) 

1299 else: 

1300 return r"C%s" % tex 

1301 

1302 def _print_subfactorial(self, expr, exp=None): 

1303 tex = r"!%s" % self.parenthesize(expr.args[0], PRECEDENCE["Func"]) 

1304 

1305 if exp is not None: 

1306 return r"\left(%s\right)^{%s}" % (tex, exp) 

1307 else: 

1308 return tex 

1309 

1310 def _print_factorial(self, expr, exp=None): 

1311 tex = r"%s!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"]) 

1312 

1313 if exp is not None: 

1314 return r"%s^{%s}" % (tex, exp) 

1315 else: 

1316 return tex 

1317 

1318 def _print_factorial2(self, expr, exp=None): 

1319 tex = r"%s!!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"]) 

1320 

1321 if exp is not None: 

1322 return r"%s^{%s}" % (tex, exp) 

1323 else: 

1324 return tex 

1325 

1326 def _print_binomial(self, expr, exp=None): 

1327 tex = r"{\binom{%s}{%s}}" % (self._print(expr.args[0]), 

1328 self._print(expr.args[1])) 

1329 

1330 if exp is not None: 

1331 return r"%s^{%s}" % (tex, exp) 

1332 else: 

1333 return tex 

1334 

1335 def _print_RisingFactorial(self, expr, exp=None): 

1336 n, k = expr.args 

1337 base = r"%s" % self.parenthesize(n, PRECEDENCE['Func']) 

1338 

1339 tex = r"{%s}^{\left(%s\right)}" % (base, self._print(k)) 

1340 

1341 return self._do_exponent(tex, exp) 

1342 

1343 def _print_FallingFactorial(self, expr, exp=None): 

1344 n, k = expr.args 

1345 sub = r"%s" % self.parenthesize(k, PRECEDENCE['Func']) 

1346 

1347 tex = r"{\left(%s\right)}_{%s}" % (self._print(n), sub) 

1348 

1349 return self._do_exponent(tex, exp) 

1350 

1351 def _hprint_BesselBase(self, expr, exp, sym: str) -> str: 

1352 tex = r"%s" % (sym) 

1353 

1354 need_exp = False 

1355 if exp is not None: 

1356 if tex.find('^') == -1: 

1357 tex = r"%s^{%s}" % (tex, exp) 

1358 else: 

1359 need_exp = True 

1360 

1361 tex = r"%s_{%s}\left(%s\right)" % (tex, self._print(expr.order), 

1362 self._print(expr.argument)) 

1363 

1364 if need_exp: 

1365 tex = self._do_exponent(tex, exp) 

1366 return tex 

1367 

1368 def _hprint_vec(self, vec) -> str: 

1369 if not vec: 

1370 return "" 

1371 s = "" 

1372 for i in vec[:-1]: 

1373 s += "%s, " % self._print(i) 

1374 s += self._print(vec[-1]) 

1375 return s 

1376 

1377 def _print_besselj(self, expr, exp=None): 

1378 return self._hprint_BesselBase(expr, exp, 'J') 

1379 

1380 def _print_besseli(self, expr, exp=None): 

1381 return self._hprint_BesselBase(expr, exp, 'I') 

1382 

1383 def _print_besselk(self, expr, exp=None): 

1384 return self._hprint_BesselBase(expr, exp, 'K') 

1385 

1386 def _print_bessely(self, expr, exp=None): 

1387 return self._hprint_BesselBase(expr, exp, 'Y') 

1388 

1389 def _print_yn(self, expr, exp=None): 

1390 return self._hprint_BesselBase(expr, exp, 'y') 

1391 

1392 def _print_jn(self, expr, exp=None): 

1393 return self._hprint_BesselBase(expr, exp, 'j') 

1394 

1395 def _print_hankel1(self, expr, exp=None): 

1396 return self._hprint_BesselBase(expr, exp, 'H^{(1)}') 

1397 

1398 def _print_hankel2(self, expr, exp=None): 

1399 return self._hprint_BesselBase(expr, exp, 'H^{(2)}') 

1400 

1401 def _print_hn1(self, expr, exp=None): 

1402 return self._hprint_BesselBase(expr, exp, 'h^{(1)}') 

1403 

1404 def _print_hn2(self, expr, exp=None): 

1405 return self._hprint_BesselBase(expr, exp, 'h^{(2)}') 

1406 

1407 def _hprint_airy(self, expr, exp=None, notation="") -> str: 

1408 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1409 

1410 if exp is not None: 

1411 return r"%s^{%s}%s" % (notation, exp, tex) 

1412 else: 

1413 return r"%s%s" % (notation, tex) 

1414 

1415 def _hprint_airy_prime(self, expr, exp=None, notation="") -> str: 

1416 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1417 

1418 if exp is not None: 

1419 return r"{%s^\prime}^{%s}%s" % (notation, exp, tex) 

1420 else: 

1421 return r"%s^\prime%s" % (notation, tex) 

1422 

1423 def _print_airyai(self, expr, exp=None): 

1424 return self._hprint_airy(expr, exp, 'Ai') 

1425 

1426 def _print_airybi(self, expr, exp=None): 

1427 return self._hprint_airy(expr, exp, 'Bi') 

1428 

1429 def _print_airyaiprime(self, expr, exp=None): 

1430 return self._hprint_airy_prime(expr, exp, 'Ai') 

1431 

1432 def _print_airybiprime(self, expr, exp=None): 

1433 return self._hprint_airy_prime(expr, exp, 'Bi') 

1434 

1435 def _print_hyper(self, expr, exp=None): 

1436 tex = r"{{}_{%s}F_{%s}\left(\begin{matrix} %s \\ %s \end{matrix}" \ 

1437 r"\middle| {%s} \right)}" % \ 

1438 (self._print(len(expr.ap)), self._print(len(expr.bq)), 

1439 self._hprint_vec(expr.ap), self._hprint_vec(expr.bq), 

1440 self._print(expr.argument)) 

1441 

1442 if exp is not None: 

1443 tex = r"{%s}^{%s}" % (tex, exp) 

1444 return tex 

1445 

1446 def _print_meijerg(self, expr, exp=None): 

1447 tex = r"{G_{%s, %s}^{%s, %s}\left(\begin{matrix} %s & %s \\" \ 

1448 r"%s & %s \end{matrix} \middle| {%s} \right)}" % \ 

1449 (self._print(len(expr.ap)), self._print(len(expr.bq)), 

1450 self._print(len(expr.bm)), self._print(len(expr.an)), 

1451 self._hprint_vec(expr.an), self._hprint_vec(expr.aother), 

1452 self._hprint_vec(expr.bm), self._hprint_vec(expr.bother), 

1453 self._print(expr.argument)) 

1454 

1455 if exp is not None: 

1456 tex = r"{%s}^{%s}" % (tex, exp) 

1457 return tex 

1458 

1459 def _print_dirichlet_eta(self, expr, exp=None): 

1460 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1461 if exp is not None: 

1462 return r"\eta^{%s}%s" % (exp, tex) 

1463 return r"\eta%s" % tex 

1464 

1465 def _print_zeta(self, expr, exp=None): 

1466 if len(expr.args) == 2: 

1467 tex = r"\left(%s, %s\right)" % tuple(map(self._print, expr.args)) 

1468 else: 

1469 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

1470 if exp is not None: 

1471 return r"\zeta^{%s}%s" % (exp, tex) 

1472 return r"\zeta%s" % tex 

1473 

1474 def _print_stieltjes(self, expr, exp=None): 

1475 if len(expr.args) == 2: 

1476 tex = r"_{%s}\left(%s\right)" % tuple(map(self._print, expr.args)) 

1477 else: 

1478 tex = r"_{%s}" % self._print(expr.args[0]) 

1479 if exp is not None: 

1480 return r"\gamma%s^{%s}" % (tex, exp) 

1481 return r"\gamma%s" % tex 

1482 

1483 def _print_lerchphi(self, expr, exp=None): 

1484 tex = r"\left(%s, %s, %s\right)" % tuple(map(self._print, expr.args)) 

1485 if exp is None: 

1486 return r"\Phi%s" % tex 

1487 return r"\Phi^{%s}%s" % (exp, tex) 

1488 

1489 def _print_polylog(self, expr, exp=None): 

1490 s, z = map(self._print, expr.args) 

1491 tex = r"\left(%s\right)" % z 

1492 if exp is None: 

1493 return r"\operatorname{Li}_{%s}%s" % (s, tex) 

1494 return r"\operatorname{Li}_{%s}^{%s}%s" % (s, exp, tex) 

1495 

1496 def _print_jacobi(self, expr, exp=None): 

1497 n, a, b, x = map(self._print, expr.args) 

1498 tex = r"P_{%s}^{\left(%s,%s\right)}\left(%s\right)" % (n, a, b, x) 

1499 if exp is not None: 

1500 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1501 return tex 

1502 

1503 def _print_gegenbauer(self, expr, exp=None): 

1504 n, a, x = map(self._print, expr.args) 

1505 tex = r"C_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) 

1506 if exp is not None: 

1507 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1508 return tex 

1509 

1510 def _print_chebyshevt(self, expr, exp=None): 

1511 n, x = map(self._print, expr.args) 

1512 tex = r"T_{%s}\left(%s\right)" % (n, x) 

1513 if exp is not None: 

1514 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1515 return tex 

1516 

1517 def _print_chebyshevu(self, expr, exp=None): 

1518 n, x = map(self._print, expr.args) 

1519 tex = r"U_{%s}\left(%s\right)" % (n, x) 

1520 if exp is not None: 

1521 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1522 return tex 

1523 

1524 def _print_legendre(self, expr, exp=None): 

1525 n, x = map(self._print, expr.args) 

1526 tex = r"P_{%s}\left(%s\right)" % (n, x) 

1527 if exp is not None: 

1528 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1529 return tex 

1530 

1531 def _print_assoc_legendre(self, expr, exp=None): 

1532 n, a, x = map(self._print, expr.args) 

1533 tex = r"P_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) 

1534 if exp is not None: 

1535 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1536 return tex 

1537 

1538 def _print_hermite(self, expr, exp=None): 

1539 n, x = map(self._print, expr.args) 

1540 tex = r"H_{%s}\left(%s\right)" % (n, x) 

1541 if exp is not None: 

1542 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1543 return tex 

1544 

1545 def _print_laguerre(self, expr, exp=None): 

1546 n, x = map(self._print, expr.args) 

1547 tex = r"L_{%s}\left(%s\right)" % (n, x) 

1548 if exp is not None: 

1549 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1550 return tex 

1551 

1552 def _print_assoc_laguerre(self, expr, exp=None): 

1553 n, a, x = map(self._print, expr.args) 

1554 tex = r"L_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x) 

1555 if exp is not None: 

1556 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1557 return tex 

1558 

1559 def _print_Ynm(self, expr, exp=None): 

1560 n, m, theta, phi = map(self._print, expr.args) 

1561 tex = r"Y_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi) 

1562 if exp is not None: 

1563 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1564 return tex 

1565 

1566 def _print_Znm(self, expr, exp=None): 

1567 n, m, theta, phi = map(self._print, expr.args) 

1568 tex = r"Z_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi) 

1569 if exp is not None: 

1570 tex = r"\left(" + tex + r"\right)^{%s}" % (exp) 

1571 return tex 

1572 

1573 def __print_mathieu_functions(self, character, args, prime=False, exp=None): 

1574 a, q, z = map(self._print, args) 

1575 sup = r"^{\prime}" if prime else "" 

1576 exp = "" if not exp else "^{%s}" % exp 

1577 return r"%s%s\left(%s, %s, %s\right)%s" % (character, sup, a, q, z, exp) 

1578 

1579 def _print_mathieuc(self, expr, exp=None): 

1580 return self.__print_mathieu_functions("C", expr.args, exp=exp) 

1581 

1582 def _print_mathieus(self, expr, exp=None): 

1583 return self.__print_mathieu_functions("S", expr.args, exp=exp) 

1584 

1585 def _print_mathieucprime(self, expr, exp=None): 

1586 return self.__print_mathieu_functions("C", expr.args, prime=True, exp=exp) 

1587 

1588 def _print_mathieusprime(self, expr, exp=None): 

1589 return self.__print_mathieu_functions("S", expr.args, prime=True, exp=exp) 

1590 

1591 def _print_Rational(self, expr): 

1592 if expr.q != 1: 

1593 sign = "" 

1594 p = expr.p 

1595 if expr.p < 0: 

1596 sign = "- " 

1597 p = -p 

1598 if self._settings['fold_short_frac']: 

1599 return r"%s%d / %d" % (sign, p, expr.q) 

1600 return r"%s\frac{%d}{%d}" % (sign, p, expr.q) 

1601 else: 

1602 return self._print(expr.p) 

1603 

1604 def _print_Order(self, expr): 

1605 s = self._print(expr.expr) 

1606 if expr.point and any(p != S.Zero for p in expr.point) or \ 

1607 len(expr.variables) > 1: 

1608 s += '; ' 

1609 if len(expr.variables) > 1: 

1610 s += self._print(expr.variables) 

1611 elif expr.variables: 

1612 s += self._print(expr.variables[0]) 

1613 s += r'\rightarrow ' 

1614 if len(expr.point) > 1: 

1615 s += self._print(expr.point) 

1616 else: 

1617 s += self._print(expr.point[0]) 

1618 return r"O\left(%s\right)" % s 

1619 

1620 def _print_Symbol(self, expr: Symbol, style='plain'): 

1621 name: str = self._settings['symbol_names'].get(expr) 

1622 if name is not None: 

1623 return name 

1624 

1625 return self._deal_with_super_sub(expr.name, style=style) 

1626 

1627 _print_RandomSymbol = _print_Symbol 

1628 

1629 def _deal_with_super_sub(self, string: str, style='plain') -> str: 

1630 if '{' in string: 

1631 name, supers, subs = string, [], [] 

1632 else: 

1633 name, supers, subs = split_super_sub(string) 

1634 

1635 name = translate(name) 

1636 supers = [translate(sup) for sup in supers] 

1637 subs = [translate(sub) for sub in subs] 

1638 

1639 # apply the style only to the name 

1640 if style == 'bold': 

1641 name = "\\mathbf{{{}}}".format(name) 

1642 

1643 # glue all items together: 

1644 if supers: 

1645 name += "^{%s}" % " ".join(supers) 

1646 if subs: 

1647 name += "_{%s}" % " ".join(subs) 

1648 

1649 return name 

1650 

1651 def _print_Relational(self, expr): 

1652 if self._settings['itex']: 

1653 gt = r"\gt" 

1654 lt = r"\lt" 

1655 else: 

1656 gt = ">" 

1657 lt = "<" 

1658 

1659 charmap = { 

1660 "==": "=", 

1661 ">": gt, 

1662 "<": lt, 

1663 ">=": r"\geq", 

1664 "<=": r"\leq", 

1665 "!=": r"\neq", 

1666 } 

1667 

1668 return "%s %s %s" % (self._print(expr.lhs), 

1669 charmap[expr.rel_op], self._print(expr.rhs)) 

1670 

1671 def _print_Piecewise(self, expr): 

1672 ecpairs = [r"%s & \text{for}\: %s" % (self._print(e), self._print(c)) 

1673 for e, c in expr.args[:-1]] 

1674 if expr.args[-1].cond == true: 

1675 ecpairs.append(r"%s & \text{otherwise}" % 

1676 self._print(expr.args[-1].expr)) 

1677 else: 

1678 ecpairs.append(r"%s & \text{for}\: %s" % 

1679 (self._print(expr.args[-1].expr), 

1680 self._print(expr.args[-1].cond))) 

1681 tex = r"\begin{cases} %s \end{cases}" 

1682 return tex % r" \\".join(ecpairs) 

1683 

1684 def _print_matrix_contents(self, expr): 

1685 lines = [] 

1686 

1687 for line in range(expr.rows): # horrible, should be 'rows' 

1688 lines.append(" & ".join([self._print(i) for i in expr[line, :]])) 

1689 

1690 mat_str = self._settings['mat_str'] 

1691 if mat_str is None: 

1692 if self._settings['mode'] == 'inline': 

1693 mat_str = 'smallmatrix' 

1694 else: 

1695 if (expr.cols <= 10) is True: 

1696 mat_str = 'matrix' 

1697 else: 

1698 mat_str = 'array' 

1699 

1700 out_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}' 

1701 out_str = out_str.replace('%MATSTR%', mat_str) 

1702 if mat_str == 'array': 

1703 out_str = out_str.replace('%s', '{' + 'c'*expr.cols + '}%s') 

1704 return out_str % r"\\".join(lines) 

1705 

1706 def _print_MatrixBase(self, expr): 

1707 out_str = self._print_matrix_contents(expr) 

1708 if self._settings['mat_delim']: 

1709 left_delim = self._settings['mat_delim'] 

1710 right_delim = self._delim_dict[left_delim] 

1711 out_str = r'\left' + left_delim + out_str + \ 

1712 r'\right' + right_delim 

1713 return out_str 

1714 

1715 def _print_MatrixElement(self, expr): 

1716 return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True)\ 

1717 + '_{%s, %s}' % (self._print(expr.i), self._print(expr.j)) 

1718 

1719 def _print_MatrixSlice(self, expr): 

1720 def latexslice(x, dim): 

1721 x = list(x) 

1722 if x[2] == 1: 

1723 del x[2] 

1724 if x[0] == 0: 

1725 x[0] = None 

1726 if x[1] == dim: 

1727 x[1] = None 

1728 return ':'.join(self._print(xi) if xi is not None else '' for xi in x) 

1729 return (self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) + r'\left[' + 

1730 latexslice(expr.rowslice, expr.parent.rows) + ', ' + 

1731 latexslice(expr.colslice, expr.parent.cols) + r'\right]') 

1732 

1733 def _print_BlockMatrix(self, expr): 

1734 return self._print(expr.blocks) 

1735 

1736 def _print_Transpose(self, expr): 

1737 mat = expr.arg 

1738 from sympy.matrices import MatrixSymbol, BlockMatrix 

1739 if (not isinstance(mat, MatrixSymbol) and 

1740 not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr): 

1741 return r"\left(%s\right)^{T}" % self._print(mat) 

1742 else: 

1743 s = self.parenthesize(mat, precedence_traditional(expr), True) 

1744 if '^' in s: 

1745 return r"\left(%s\right)^{T}" % s 

1746 else: 

1747 return "%s^{T}" % s 

1748 

1749 def _print_Trace(self, expr): 

1750 mat = expr.arg 

1751 return r"\operatorname{tr}\left(%s \right)" % self._print(mat) 

1752 

1753 def _print_Adjoint(self, expr): 

1754 mat = expr.arg 

1755 from sympy.matrices import MatrixSymbol, BlockMatrix 

1756 if (not isinstance(mat, MatrixSymbol) and 

1757 not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr): 

1758 return r"\left(%s\right)^{\dagger}" % self._print(mat) 

1759 else: 

1760 s = self.parenthesize(mat, precedence_traditional(expr), True) 

1761 if '^' in s: 

1762 return r"\left(%s\right)^{\dagger}" % s 

1763 else: 

1764 return r"%s^{\dagger}" % s 

1765 

1766 def _print_MatMul(self, expr): 

1767 from sympy import MatMul 

1768 

1769 # Parenthesize nested MatMul but not other types of Mul objects: 

1770 parens = lambda x: self._print(x) if isinstance(x, Mul) and not isinstance(x, MatMul) else \ 

1771 self.parenthesize(x, precedence_traditional(expr), False) 

1772 

1773 args = list(expr.args) 

1774 if expr.could_extract_minus_sign(): 

1775 if args[0] == -1: 

1776 args = args[1:] 

1777 else: 

1778 args[0] = -args[0] 

1779 return '- ' + ' '.join(map(parens, args)) 

1780 else: 

1781 return ' '.join(map(parens, args)) 

1782 

1783 def _print_Determinant(self, expr): 

1784 mat = expr.arg 

1785 if mat.is_MatrixExpr: 

1786 from sympy.matrices.expressions.blockmatrix import BlockMatrix 

1787 if isinstance(mat, BlockMatrix): 

1788 return r"\left|{%s}\right|" % self._print_matrix_contents(mat.blocks) 

1789 return r"\left|{%s}\right|" % self._print(mat) 

1790 return r"\left|{%s}\right|" % self._print_matrix_contents(mat) 

1791 

1792 

1793 def _print_Mod(self, expr, exp=None): 

1794 if exp is not None: 

1795 return r'\left(%s \bmod %s\right)^{%s}' % \ 

1796 (self.parenthesize(expr.args[0], PRECEDENCE['Mul'], 

1797 strict=True), 

1798 self.parenthesize(expr.args[1], PRECEDENCE['Mul'], 

1799 strict=True), 

1800 exp) 

1801 return r'%s \bmod %s' % (self.parenthesize(expr.args[0], 

1802 PRECEDENCE['Mul'], 

1803 strict=True), 

1804 self.parenthesize(expr.args[1], 

1805 PRECEDENCE['Mul'], 

1806 strict=True)) 

1807 

1808 def _print_HadamardProduct(self, expr): 

1809 args = expr.args 

1810 prec = PRECEDENCE['Pow'] 

1811 parens = self.parenthesize 

1812 

1813 return r' \circ '.join( 

1814 (parens(arg, prec, strict=True) for arg in args)) 

1815 

1816 def _print_HadamardPower(self, expr): 

1817 if precedence_traditional(expr.exp) < PRECEDENCE["Mul"]: 

1818 template = r"%s^{\circ \left({%s}\right)}" 

1819 else: 

1820 template = r"%s^{\circ {%s}}" 

1821 return self._helper_print_standard_power(expr, template) 

1822 

1823 def _print_KroneckerProduct(self, expr): 

1824 args = expr.args 

1825 prec = PRECEDENCE['Pow'] 

1826 parens = self.parenthesize 

1827 

1828 return r' \otimes '.join( 

1829 (parens(arg, prec, strict=True) for arg in args)) 

1830 

1831 def _print_MatPow(self, expr): 

1832 base, exp = expr.base, expr.exp 

1833 from sympy.matrices import MatrixSymbol 

1834 if not isinstance(base, MatrixSymbol) and base.is_MatrixExpr: 

1835 return "\\left(%s\\right)^{%s}" % (self._print(base), 

1836 self._print(exp)) 

1837 else: 

1838 base_str = self._print(base) 

1839 if '^' in base_str: 

1840 return r"\left(%s\right)^{%s}" % (base_str, self._print(exp)) 

1841 else: 

1842 return "%s^{%s}" % (base_str, self._print(exp)) 

1843 

1844 def _print_MatrixSymbol(self, expr): 

1845 return self._print_Symbol(expr, style=self._settings[ 

1846 'mat_symbol_style']) 

1847 

1848 def _print_ZeroMatrix(self, Z): 

1849 return "0" if self._settings[ 

1850 'mat_symbol_style'] == 'plain' else r"\mathbf{0}" 

1851 

1852 def _print_OneMatrix(self, O): 

1853 return "1" if self._settings[ 

1854 'mat_symbol_style'] == 'plain' else r"\mathbf{1}" 

1855 

1856 def _print_Identity(self, I): 

1857 return r"\mathbb{I}" if self._settings[ 

1858 'mat_symbol_style'] == 'plain' else r"\mathbf{I}" 

1859 

1860 def _print_PermutationMatrix(self, P): 

1861 perm_str = self._print(P.args[0]) 

1862 return "P_{%s}" % perm_str 

1863 

1864 def _print_NDimArray(self, expr: NDimArray): 

1865 

1866 if expr.rank() == 0: 

1867 return self._print(expr[()]) 

1868 

1869 mat_str = self._settings['mat_str'] 

1870 if mat_str is None: 

1871 if self._settings['mode'] == 'inline': 

1872 mat_str = 'smallmatrix' 

1873 else: 

1874 if (expr.rank() == 0) or (expr.shape[-1] <= 10): 

1875 mat_str = 'matrix' 

1876 else: 

1877 mat_str = 'array' 

1878 block_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}' 

1879 block_str = block_str.replace('%MATSTR%', mat_str) 

1880 if mat_str == 'array': 

1881 block_str= block_str.replace('%s','{}%s') 

1882 if self._settings['mat_delim']: 

1883 left_delim: str = self._settings['mat_delim'] 

1884 right_delim = self._delim_dict[left_delim] 

1885 block_str = r'\left' + left_delim + block_str + \ 

1886 r'\right' + right_delim 

1887 

1888 if expr.rank() == 0: 

1889 return block_str % "" 

1890 

1891 level_str: list[list[str]] = [[] for i in range(expr.rank() + 1)] 

1892 shape_ranges = [list(range(i)) for i in expr.shape] 

1893 for outer_i in itertools.product(*shape_ranges): 

1894 level_str[-1].append(self._print(expr[outer_i])) 

1895 even = True 

1896 for back_outer_i in range(expr.rank()-1, -1, -1): 

1897 if len(level_str[back_outer_i+1]) < expr.shape[back_outer_i]: 

1898 break 

1899 if even: 

1900 level_str[back_outer_i].append( 

1901 r" & ".join(level_str[back_outer_i+1])) 

1902 else: 

1903 level_str[back_outer_i].append( 

1904 block_str % (r"\\".join(level_str[back_outer_i+1]))) 

1905 if len(level_str[back_outer_i+1]) == 1: 

1906 level_str[back_outer_i][-1] = r"\left[" + \ 

1907 level_str[back_outer_i][-1] + r"\right]" 

1908 even = not even 

1909 level_str[back_outer_i+1] = [] 

1910 

1911 out_str = level_str[0][0] 

1912 

1913 if expr.rank() % 2 == 1: 

1914 out_str = block_str % out_str 

1915 

1916 return out_str 

1917 

1918 def _printer_tensor_indices(self, name, indices, index_map: dict): 

1919 out_str = self._print(name) 

1920 last_valence = None 

1921 prev_map = None 

1922 for index in indices: 

1923 new_valence = index.is_up 

1924 if ((index in index_map) or prev_map) and \ 

1925 last_valence == new_valence: 

1926 out_str += "," 

1927 if last_valence != new_valence: 

1928 if last_valence is not None: 

1929 out_str += "}" 

1930 if index.is_up: 

1931 out_str += "{}^{" 

1932 else: 

1933 out_str += "{}_{" 

1934 out_str += self._print(index.args[0]) 

1935 if index in index_map: 

1936 out_str += "=" 

1937 out_str += self._print(index_map[index]) 

1938 prev_map = True 

1939 else: 

1940 prev_map = False 

1941 last_valence = new_valence 

1942 if last_valence is not None: 

1943 out_str += "}" 

1944 return out_str 

1945 

1946 def _print_Tensor(self, expr): 

1947 name = expr.args[0].args[0] 

1948 indices = expr.get_indices() 

1949 return self._printer_tensor_indices(name, indices, {}) 

1950 

1951 def _print_TensorElement(self, expr): 

1952 name = expr.expr.args[0].args[0] 

1953 indices = expr.expr.get_indices() 

1954 index_map = expr.index_map 

1955 return self._printer_tensor_indices(name, indices, index_map) 

1956 

1957 def _print_TensMul(self, expr): 

1958 # prints expressions like "A(a)", "3*A(a)", "(1+x)*A(a)" 

1959 sign, args = expr._get_args_for_traditional_printer() 

1960 return sign + "".join( 

1961 [self.parenthesize(arg, precedence(expr)) for arg in args] 

1962 ) 

1963 

1964 def _print_TensAdd(self, expr): 

1965 a = [] 

1966 args = expr.args 

1967 for x in args: 

1968 a.append(self.parenthesize(x, precedence(expr))) 

1969 a.sort() 

1970 s = ' + '.join(a) 

1971 s = s.replace('+ -', '- ') 

1972 return s 

1973 

1974 def _print_TensorIndex(self, expr): 

1975 return "{}%s{%s}" % ( 

1976 "^" if expr.is_up else "_", 

1977 self._print(expr.args[0]) 

1978 ) 

1979 

1980 def _print_PartialDerivative(self, expr): 

1981 if len(expr.variables) == 1: 

1982 return r"\frac{\partial}{\partial {%s}}{%s}" % ( 

1983 self._print(expr.variables[0]), 

1984 self.parenthesize(expr.expr, PRECEDENCE["Mul"], False) 

1985 ) 

1986 else: 

1987 return r"\frac{\partial^{%s}}{%s}{%s}" % ( 

1988 len(expr.variables), 

1989 " ".join([r"\partial {%s}" % self._print(i) for i in expr.variables]), 

1990 self.parenthesize(expr.expr, PRECEDENCE["Mul"], False) 

1991 ) 

1992 

1993 def _print_ArraySymbol(self, expr): 

1994 return self._print(expr.name) 

1995 

1996 def _print_ArrayElement(self, expr): 

1997 return "{{%s}_{%s}}" % ( 

1998 self.parenthesize(expr.name, PRECEDENCE["Func"], True), 

1999 ", ".join([f"{self._print(i)}" for i in expr.indices])) 

2000 

2001 def _print_UniversalSet(self, expr): 

2002 return r"\mathbb{U}" 

2003 

2004 def _print_frac(self, expr, exp=None): 

2005 if exp is None: 

2006 return r"\operatorname{frac}{\left(%s\right)}" % self._print(expr.args[0]) 

2007 else: 

2008 return r"\operatorname{frac}{\left(%s\right)}^{%s}" % ( 

2009 self._print(expr.args[0]), exp) 

2010 

2011 def _print_tuple(self, expr): 

2012 if self._settings['decimal_separator'] == 'comma': 

2013 sep = ";" 

2014 elif self._settings['decimal_separator'] == 'period': 

2015 sep = "," 

2016 else: 

2017 raise ValueError('Unknown Decimal Separator') 

2018 

2019 if len(expr) == 1: 

2020 # 1-tuple needs a trailing separator 

2021 return self._add_parens_lspace(self._print(expr[0]) + sep) 

2022 else: 

2023 return self._add_parens_lspace( 

2024 (sep + r" \ ").join([self._print(i) for i in expr])) 

2025 

2026 def _print_TensorProduct(self, expr): 

2027 elements = [self._print(a) for a in expr.args] 

2028 return r' \otimes '.join(elements) 

2029 

2030 def _print_WedgeProduct(self, expr): 

2031 elements = [self._print(a) for a in expr.args] 

2032 return r' \wedge '.join(elements) 

2033 

2034 def _print_Tuple(self, expr): 

2035 return self._print_tuple(expr) 

2036 

2037 def _print_list(self, expr): 

2038 if self._settings['decimal_separator'] == 'comma': 

2039 return r"\left[ %s\right]" % \ 

2040 r"; \ ".join([self._print(i) for i in expr]) 

2041 elif self._settings['decimal_separator'] == 'period': 

2042 return r"\left[ %s\right]" % \ 

2043 r", \ ".join([self._print(i) for i in expr]) 

2044 else: 

2045 raise ValueError('Unknown Decimal Separator') 

2046 

2047 

2048 def _print_dict(self, d): 

2049 keys = sorted(d.keys(), key=default_sort_key) 

2050 items = [] 

2051 

2052 for key in keys: 

2053 val = d[key] 

2054 items.append("%s : %s" % (self._print(key), self._print(val))) 

2055 

2056 return r"\left\{ %s\right\}" % r", \ ".join(items) 

2057 

2058 def _print_Dict(self, expr): 

2059 return self._print_dict(expr) 

2060 

2061 def _print_DiracDelta(self, expr, exp=None): 

2062 if len(expr.args) == 1 or expr.args[1] == 0: 

2063 tex = r"\delta\left(%s\right)" % self._print(expr.args[0]) 

2064 else: 

2065 tex = r"\delta^{\left( %s \right)}\left( %s \right)" % ( 

2066 self._print(expr.args[1]), self._print(expr.args[0])) 

2067 if exp: 

2068 tex = r"\left(%s\right)^{%s}" % (tex, exp) 

2069 return tex 

2070 

2071 def _print_SingularityFunction(self, expr, exp=None): 

2072 shift = self._print(expr.args[0] - expr.args[1]) 

2073 power = self._print(expr.args[2]) 

2074 tex = r"{\left\langle %s \right\rangle}^{%s}" % (shift, power) 

2075 if exp is not None: 

2076 tex = r"{\left({\langle %s \rangle}^{%s}\right)}^{%s}" % (shift, power, exp) 

2077 return tex 

2078 

2079 def _print_Heaviside(self, expr, exp=None): 

2080 pargs = ', '.join(self._print(arg) for arg in expr.pargs) 

2081 tex = r"\theta\left(%s\right)" % pargs 

2082 if exp: 

2083 tex = r"\left(%s\right)^{%s}" % (tex, exp) 

2084 return tex 

2085 

2086 def _print_KroneckerDelta(self, expr, exp=None): 

2087 i = self._print(expr.args[0]) 

2088 j = self._print(expr.args[1]) 

2089 if expr.args[0].is_Atom and expr.args[1].is_Atom: 

2090 tex = r'\delta_{%s %s}' % (i, j) 

2091 else: 

2092 tex = r'\delta_{%s, %s}' % (i, j) 

2093 if exp is not None: 

2094 tex = r'\left(%s\right)^{%s}' % (tex, exp) 

2095 return tex 

2096 

2097 def _print_LeviCivita(self, expr, exp=None): 

2098 indices = map(self._print, expr.args) 

2099 if all(x.is_Atom for x in expr.args): 

2100 tex = r'\varepsilon_{%s}' % " ".join(indices) 

2101 else: 

2102 tex = r'\varepsilon_{%s}' % ", ".join(indices) 

2103 if exp: 

2104 tex = r'\left(%s\right)^{%s}' % (tex, exp) 

2105 return tex 

2106 

2107 def _print_RandomDomain(self, d): 

2108 if hasattr(d, 'as_boolean'): 

2109 return '\\text{Domain: }' + self._print(d.as_boolean()) 

2110 elif hasattr(d, 'set'): 

2111 return ('\\text{Domain: }' + self._print(d.symbols) + ' \\in ' + 

2112 self._print(d.set)) 

2113 elif hasattr(d, 'symbols'): 

2114 return '\\text{Domain on }' + self._print(d.symbols) 

2115 else: 

2116 return self._print(None) 

2117 

2118 def _print_FiniteSet(self, s): 

2119 items = sorted(s.args, key=default_sort_key) 

2120 return self._print_set(items) 

2121 

2122 def _print_set(self, s): 

2123 items = sorted(s, key=default_sort_key) 

2124 if self._settings['decimal_separator'] == 'comma': 

2125 items = "; ".join(map(self._print, items)) 

2126 elif self._settings['decimal_separator'] == 'period': 

2127 items = ", ".join(map(self._print, items)) 

2128 else: 

2129 raise ValueError('Unknown Decimal Separator') 

2130 return r"\left\{%s\right\}" % items 

2131 

2132 

2133 _print_frozenset = _print_set 

2134 

2135 def _print_Range(self, s): 

2136 def _print_symbolic_range(): 

2137 # Symbolic Range that cannot be resolved 

2138 if s.args[0] == 0: 

2139 if s.args[2] == 1: 

2140 cont = self._print(s.args[1]) 

2141 else: 

2142 cont = ", ".join(self._print(arg) for arg in s.args) 

2143 else: 

2144 if s.args[2] == 1: 

2145 cont = ", ".join(self._print(arg) for arg in s.args[:2]) 

2146 else: 

2147 cont = ", ".join(self._print(arg) for arg in s.args) 

2148 

2149 return(f"\\text{{Range}}\\left({cont}\\right)") 

2150 

2151 dots = object() 

2152 

2153 if s.start.is_infinite and s.stop.is_infinite: 

2154 if s.step.is_positive: 

2155 printset = dots, -1, 0, 1, dots 

2156 else: 

2157 printset = dots, 1, 0, -1, dots 

2158 elif s.start.is_infinite: 

2159 printset = dots, s[-1] - s.step, s[-1] 

2160 elif s.stop.is_infinite: 

2161 it = iter(s) 

2162 printset = next(it), next(it), dots 

2163 elif s.is_empty is not None: 

2164 if (s.size < 4) == True: 

2165 printset = tuple(s) 

2166 elif s.is_iterable: 

2167 it = iter(s) 

2168 printset = next(it), next(it), dots, s[-1] 

2169 else: 

2170 return _print_symbolic_range() 

2171 else: 

2172 return _print_symbolic_range() 

2173 return (r"\left\{" + 

2174 r", ".join(self._print(el) if el is not dots else r'\ldots' for el in printset) + 

2175 r"\right\}") 

2176 

2177 def __print_number_polynomial(self, expr, letter, exp=None): 

2178 if len(expr.args) == 2: 

2179 if exp is not None: 

2180 return r"%s_{%s}^{%s}\left(%s\right)" % (letter, 

2181 self._print(expr.args[0]), exp, 

2182 self._print(expr.args[1])) 

2183 return r"%s_{%s}\left(%s\right)" % (letter, 

2184 self._print(expr.args[0]), self._print(expr.args[1])) 

2185 

2186 tex = r"%s_{%s}" % (letter, self._print(expr.args[0])) 

2187 if exp is not None: 

2188 tex = r"%s^{%s}" % (tex, exp) 

2189 return tex 

2190 

2191 def _print_bernoulli(self, expr, exp=None): 

2192 return self.__print_number_polynomial(expr, "B", exp) 

2193 

2194 def _print_genocchi(self, expr, exp=None): 

2195 return self.__print_number_polynomial(expr, "G", exp) 

2196 

2197 def _print_bell(self, expr, exp=None): 

2198 if len(expr.args) == 3: 

2199 tex1 = r"B_{%s, %s}" % (self._print(expr.args[0]), 

2200 self._print(expr.args[1])) 

2201 tex2 = r"\left(%s\right)" % r", ".join(self._print(el) for 

2202 el in expr.args[2]) 

2203 if exp is not None: 

2204 tex = r"%s^{%s}%s" % (tex1, exp, tex2) 

2205 else: 

2206 tex = tex1 + tex2 

2207 return tex 

2208 return self.__print_number_polynomial(expr, "B", exp) 

2209 

2210 def _print_fibonacci(self, expr, exp=None): 

2211 return self.__print_number_polynomial(expr, "F", exp) 

2212 

2213 def _print_lucas(self, expr, exp=None): 

2214 tex = r"L_{%s}" % self._print(expr.args[0]) 

2215 if exp is not None: 

2216 tex = r"%s^{%s}" % (tex, exp) 

2217 return tex 

2218 

2219 def _print_tribonacci(self, expr, exp=None): 

2220 return self.__print_number_polynomial(expr, "T", exp) 

2221 

2222 def _print_SeqFormula(self, s): 

2223 dots = object() 

2224 if len(s.start.free_symbols) > 0 or len(s.stop.free_symbols) > 0: 

2225 return r"\left\{%s\right\}_{%s=%s}^{%s}" % ( 

2226 self._print(s.formula), 

2227 self._print(s.variables[0]), 

2228 self._print(s.start), 

2229 self._print(s.stop) 

2230 ) 

2231 if s.start is S.NegativeInfinity: 

2232 stop = s.stop 

2233 printset = (dots, s.coeff(stop - 3), s.coeff(stop - 2), 

2234 s.coeff(stop - 1), s.coeff(stop)) 

2235 elif s.stop is S.Infinity or s.length > 4: 

2236 printset = s[:4] 

2237 printset.append(dots) 

2238 else: 

2239 printset = tuple(s) 

2240 

2241 return (r"\left[" + 

2242 r", ".join(self._print(el) if el is not dots else r'\ldots' for el in printset) + 

2243 r"\right]") 

2244 

2245 _print_SeqPer = _print_SeqFormula 

2246 _print_SeqAdd = _print_SeqFormula 

2247 _print_SeqMul = _print_SeqFormula 

2248 

2249 def _print_Interval(self, i): 

2250 if i.start == i.end: 

2251 return r"\left\{%s\right\}" % self._print(i.start) 

2252 

2253 else: 

2254 if i.left_open: 

2255 left = '(' 

2256 else: 

2257 left = '[' 

2258 

2259 if i.right_open: 

2260 right = ')' 

2261 else: 

2262 right = ']' 

2263 

2264 return r"\left%s%s, %s\right%s" % \ 

2265 (left, self._print(i.start), self._print(i.end), right) 

2266 

2267 def _print_AccumulationBounds(self, i): 

2268 return r"\left\langle %s, %s\right\rangle" % \ 

2269 (self._print(i.min), self._print(i.max)) 

2270 

2271 def _print_Union(self, u): 

2272 prec = precedence_traditional(u) 

2273 args_str = [self.parenthesize(i, prec) for i in u.args] 

2274 return r" \cup ".join(args_str) 

2275 

2276 def _print_Complement(self, u): 

2277 prec = precedence_traditional(u) 

2278 args_str = [self.parenthesize(i, prec) for i in u.args] 

2279 return r" \setminus ".join(args_str) 

2280 

2281 def _print_Intersection(self, u): 

2282 prec = precedence_traditional(u) 

2283 args_str = [self.parenthesize(i, prec) for i in u.args] 

2284 return r" \cap ".join(args_str) 

2285 

2286 def _print_SymmetricDifference(self, u): 

2287 prec = precedence_traditional(u) 

2288 args_str = [self.parenthesize(i, prec) for i in u.args] 

2289 return r" \triangle ".join(args_str) 

2290 

2291 def _print_ProductSet(self, p): 

2292 prec = precedence_traditional(p) 

2293 if len(p.sets) >= 1 and not has_variety(p.sets): 

2294 return self.parenthesize(p.sets[0], prec) + "^{%d}" % len(p.sets) 

2295 return r" \times ".join( 

2296 self.parenthesize(set, prec) for set in p.sets) 

2297 

2298 def _print_EmptySet(self, e): 

2299 return r"\emptyset" 

2300 

2301 def _print_Naturals(self, n): 

2302 return r"\mathbb{N}" 

2303 

2304 def _print_Naturals0(self, n): 

2305 return r"\mathbb{N}_0" 

2306 

2307 def _print_Integers(self, i): 

2308 return r"\mathbb{Z}" 

2309 

2310 def _print_Rationals(self, i): 

2311 return r"\mathbb{Q}" 

2312 

2313 def _print_Reals(self, i): 

2314 return r"\mathbb{R}" 

2315 

2316 def _print_Complexes(self, i): 

2317 return r"\mathbb{C}" 

2318 

2319 def _print_ImageSet(self, s): 

2320 expr = s.lamda.expr 

2321 sig = s.lamda.signature 

2322 xys = ((self._print(x), self._print(y)) for x, y in zip(sig, s.base_sets)) 

2323 xinys = r", ".join(r"%s \in %s" % xy for xy in xys) 

2324 return r"\left\{%s\; \middle|\; %s\right\}" % (self._print(expr), xinys) 

2325 

2326 def _print_ConditionSet(self, s): 

2327 vars_print = ', '.join([self._print(var) for var in Tuple(s.sym)]) 

2328 if s.base_set is S.UniversalSet: 

2329 return r"\left\{%s\; \middle|\; %s \right\}" % \ 

2330 (vars_print, self._print(s.condition)) 

2331 

2332 return r"\left\{%s\; \middle|\; %s \in %s \wedge %s \right\}" % ( 

2333 vars_print, 

2334 vars_print, 

2335 self._print(s.base_set), 

2336 self._print(s.condition)) 

2337 

2338 def _print_PowerSet(self, expr): 

2339 arg_print = self._print(expr.args[0]) 

2340 return r"\mathcal{{P}}\left({}\right)".format(arg_print) 

2341 

2342 def _print_ComplexRegion(self, s): 

2343 vars_print = ', '.join([self._print(var) for var in s.variables]) 

2344 return r"\left\{%s\; \middle|\; %s \in %s \right\}" % ( 

2345 self._print(s.expr), 

2346 vars_print, 

2347 self._print(s.sets)) 

2348 

2349 def _print_Contains(self, e): 

2350 return r"%s \in %s" % tuple(self._print(a) for a in e.args) 

2351 

2352 def _print_FourierSeries(self, s): 

2353 if s.an.formula is S.Zero and s.bn.formula is S.Zero: 

2354 return self._print(s.a0) 

2355 return self._print_Add(s.truncate()) + r' + \ldots' 

2356 

2357 def _print_FormalPowerSeries(self, s): 

2358 return self._print_Add(s.infinite) 

2359 

2360 def _print_FiniteField(self, expr): 

2361 return r"\mathbb{F}_{%s}" % expr.mod 

2362 

2363 def _print_IntegerRing(self, expr): 

2364 return r"\mathbb{Z}" 

2365 

2366 def _print_RationalField(self, expr): 

2367 return r"\mathbb{Q}" 

2368 

2369 def _print_RealField(self, expr): 

2370 return r"\mathbb{R}" 

2371 

2372 def _print_ComplexField(self, expr): 

2373 return r"\mathbb{C}" 

2374 

2375 def _print_PolynomialRing(self, expr): 

2376 domain = self._print(expr.domain) 

2377 symbols = ", ".join(map(self._print, expr.symbols)) 

2378 return r"%s\left[%s\right]" % (domain, symbols) 

2379 

2380 def _print_FractionField(self, expr): 

2381 domain = self._print(expr.domain) 

2382 symbols = ", ".join(map(self._print, expr.symbols)) 

2383 return r"%s\left(%s\right)" % (domain, symbols) 

2384 

2385 def _print_PolynomialRingBase(self, expr): 

2386 domain = self._print(expr.domain) 

2387 symbols = ", ".join(map(self._print, expr.symbols)) 

2388 inv = "" 

2389 if not expr.is_Poly: 

2390 inv = r"S_<^{-1}" 

2391 return r"%s%s\left[%s\right]" % (inv, domain, symbols) 

2392 

2393 def _print_Poly(self, poly): 

2394 cls = poly.__class__.__name__ 

2395 terms = [] 

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

2397 s_monom = '' 

2398 for i, exp in enumerate(monom): 

2399 if exp > 0: 

2400 if exp == 1: 

2401 s_monom += self._print(poly.gens[i]) 

2402 else: 

2403 s_monom += self._print(pow(poly.gens[i], exp)) 

2404 

2405 if coeff.is_Add: 

2406 if s_monom: 

2407 s_coeff = r"\left(%s\right)" % self._print(coeff) 

2408 else: 

2409 s_coeff = self._print(coeff) 

2410 else: 

2411 if s_monom: 

2412 if coeff is S.One: 

2413 terms.extend(['+', s_monom]) 

2414 continue 

2415 

2416 if coeff is S.NegativeOne: 

2417 terms.extend(['-', s_monom]) 

2418 continue 

2419 

2420 s_coeff = self._print(coeff) 

2421 

2422 if not s_monom: 

2423 s_term = s_coeff 

2424 else: 

2425 s_term = s_coeff + " " + s_monom 

2426 

2427 if s_term.startswith('-'): 

2428 terms.extend(['-', s_term[1:]]) 

2429 else: 

2430 terms.extend(['+', s_term]) 

2431 

2432 if terms[0] in ('-', '+'): 

2433 modifier = terms.pop(0) 

2434 

2435 if modifier == '-': 

2436 terms[0] = '-' + terms[0] 

2437 

2438 expr = ' '.join(terms) 

2439 gens = list(map(self._print, poly.gens)) 

2440 domain = "domain=%s" % self._print(poly.get_domain()) 

2441 

2442 args = ", ".join([expr] + gens + [domain]) 

2443 if cls in accepted_latex_functions: 

2444 tex = r"\%s {\left(%s \right)}" % (cls, args) 

2445 else: 

2446 tex = r"\operatorname{%s}{\left( %s \right)}" % (cls, args) 

2447 

2448 return tex 

2449 

2450 def _print_ComplexRootOf(self, root): 

2451 cls = root.__class__.__name__ 

2452 if cls == "ComplexRootOf": 

2453 cls = "CRootOf" 

2454 expr = self._print(root.expr) 

2455 index = root.index 

2456 if cls in accepted_latex_functions: 

2457 return r"\%s {\left(%s, %d\right)}" % (cls, expr, index) 

2458 else: 

2459 return r"\operatorname{%s} {\left(%s, %d\right)}" % (cls, expr, 

2460 index) 

2461 

2462 def _print_RootSum(self, expr): 

2463 cls = expr.__class__.__name__ 

2464 args = [self._print(expr.expr)] 

2465 

2466 if expr.fun is not S.IdentityFunction: 

2467 args.append(self._print(expr.fun)) 

2468 

2469 if cls in accepted_latex_functions: 

2470 return r"\%s {\left(%s\right)}" % (cls, ", ".join(args)) 

2471 else: 

2472 return r"\operatorname{%s} {\left(%s\right)}" % (cls, 

2473 ", ".join(args)) 

2474 

2475 def _print_OrdinalOmega(self, expr): 

2476 return r"\omega" 

2477 

2478 def _print_OmegaPower(self, expr): 

2479 exp, mul = expr.args 

2480 if mul != 1: 

2481 if exp != 1: 

2482 return r"{} \omega^{{{}}}".format(mul, exp) 

2483 else: 

2484 return r"{} \omega".format(mul) 

2485 else: 

2486 if exp != 1: 

2487 return r"\omega^{{{}}}".format(exp) 

2488 else: 

2489 return r"\omega" 

2490 

2491 def _print_Ordinal(self, expr): 

2492 return " + ".join([self._print(arg) for arg in expr.args]) 

2493 

2494 def _print_PolyElement(self, poly): 

2495 mul_symbol = self._settings['mul_symbol_latex'] 

2496 return poly.str(self, PRECEDENCE, "{%s}^{%d}", mul_symbol) 

2497 

2498 def _print_FracElement(self, frac): 

2499 if frac.denom == 1: 

2500 return self._print(frac.numer) 

2501 else: 

2502 numer = self._print(frac.numer) 

2503 denom = self._print(frac.denom) 

2504 return r"\frac{%s}{%s}" % (numer, denom) 

2505 

2506 def _print_euler(self, expr, exp=None): 

2507 m, x = (expr.args[0], None) if len(expr.args) == 1 else expr.args 

2508 tex = r"E_{%s}" % self._print(m) 

2509 if exp is not None: 

2510 tex = r"%s^{%s}" % (tex, exp) 

2511 if x is not None: 

2512 tex = r"%s\left(%s\right)" % (tex, self._print(x)) 

2513 return tex 

2514 

2515 def _print_catalan(self, expr, exp=None): 

2516 tex = r"C_{%s}" % self._print(expr.args[0]) 

2517 if exp is not None: 

2518 tex = r"%s^{%s}" % (tex, exp) 

2519 return tex 

2520 

2521 def _print_UnifiedTransform(self, expr, s, inverse=False): 

2522 return r"\mathcal{{{}}}{}_{{{}}}\left[{}\right]\left({}\right)".format(s, '^{-1}' if inverse else '', self._print(expr.args[1]), self._print(expr.args[0]), self._print(expr.args[2])) 

2523 

2524 def _print_MellinTransform(self, expr): 

2525 return self._print_UnifiedTransform(expr, 'M') 

2526 

2527 def _print_InverseMellinTransform(self, expr): 

2528 return self._print_UnifiedTransform(expr, 'M', True) 

2529 

2530 def _print_LaplaceTransform(self, expr): 

2531 return self._print_UnifiedTransform(expr, 'L') 

2532 

2533 def _print_InverseLaplaceTransform(self, expr): 

2534 return self._print_UnifiedTransform(expr, 'L', True) 

2535 

2536 def _print_FourierTransform(self, expr): 

2537 return self._print_UnifiedTransform(expr, 'F') 

2538 

2539 def _print_InverseFourierTransform(self, expr): 

2540 return self._print_UnifiedTransform(expr, 'F', True) 

2541 

2542 def _print_SineTransform(self, expr): 

2543 return self._print_UnifiedTransform(expr, 'SIN') 

2544 

2545 def _print_InverseSineTransform(self, expr): 

2546 return self._print_UnifiedTransform(expr, 'SIN', True) 

2547 

2548 def _print_CosineTransform(self, expr): 

2549 return self._print_UnifiedTransform(expr, 'COS') 

2550 

2551 def _print_InverseCosineTransform(self, expr): 

2552 return self._print_UnifiedTransform(expr, 'COS', True) 

2553 

2554 def _print_DMP(self, p): 

2555 try: 

2556 if p.ring is not None: 

2557 # TODO incorporate order 

2558 return self._print(p.ring.to_sympy(p)) 

2559 except SympifyError: 

2560 pass 

2561 return self._print(repr(p)) 

2562 

2563 def _print_DMF(self, p): 

2564 return self._print_DMP(p) 

2565 

2566 def _print_Object(self, object): 

2567 return self._print(Symbol(object.name)) 

2568 

2569 def _print_LambertW(self, expr, exp=None): 

2570 arg0 = self._print(expr.args[0]) 

2571 exp = r"^{%s}" % (exp,) if exp is not None else "" 

2572 if len(expr.args) == 1: 

2573 result = r"W%s\left(%s\right)" % (exp, arg0) 

2574 else: 

2575 arg1 = self._print(expr.args[1]) 

2576 result = "W{0}_{{{1}}}\\left({2}\\right)".format(exp, arg1, arg0) 

2577 return result 

2578 

2579 def _print_Expectation(self, expr): 

2580 return r"\operatorname{{E}}\left[{}\right]".format(self._print(expr.args[0])) 

2581 

2582 def _print_Variance(self, expr): 

2583 return r"\operatorname{{Var}}\left({}\right)".format(self._print(expr.args[0])) 

2584 

2585 def _print_Covariance(self, expr): 

2586 return r"\operatorname{{Cov}}\left({}\right)".format(", ".join(self._print(arg) for arg in expr.args)) 

2587 

2588 def _print_Probability(self, expr): 

2589 return r"\operatorname{{P}}\left({}\right)".format(self._print(expr.args[0])) 

2590 

2591 def _print_Morphism(self, morphism): 

2592 domain = self._print(morphism.domain) 

2593 codomain = self._print(morphism.codomain) 

2594 return "%s\\rightarrow %s" % (domain, codomain) 

2595 

2596 def _print_TransferFunction(self, expr): 

2597 num, den = self._print(expr.num), self._print(expr.den) 

2598 return r"\frac{%s}{%s}" % (num, den) 

2599 

2600 def _print_Series(self, expr): 

2601 args = list(expr.args) 

2602 parens = lambda x: self.parenthesize(x, precedence_traditional(expr), 

2603 False) 

2604 return ' '.join(map(parens, args)) 

2605 

2606 def _print_MIMOSeries(self, expr): 

2607 from sympy.physics.control.lti import MIMOParallel 

2608 args = list(expr.args)[::-1] 

2609 parens = lambda x: self.parenthesize(x, precedence_traditional(expr), 

2610 False) if isinstance(x, MIMOParallel) else self._print(x) 

2611 return r"\cdot".join(map(parens, args)) 

2612 

2613 def _print_Parallel(self, expr): 

2614 return ' + '.join(map(self._print, expr.args)) 

2615 

2616 def _print_MIMOParallel(self, expr): 

2617 return ' + '.join(map(self._print, expr.args)) 

2618 

2619 def _print_Feedback(self, expr): 

2620 from sympy.physics.control import TransferFunction, Series 

2621 

2622 num, tf = expr.sys1, TransferFunction(1, 1, expr.var) 

2623 num_arg_list = list(num.args) if isinstance(num, Series) else [num] 

2624 den_arg_list = list(expr.sys2.args) if \ 

2625 isinstance(expr.sys2, Series) else [expr.sys2] 

2626 den_term_1 = tf 

2627 

2628 if isinstance(num, Series) and isinstance(expr.sys2, Series): 

2629 den_term_2 = Series(*num_arg_list, *den_arg_list) 

2630 elif isinstance(num, Series) and isinstance(expr.sys2, TransferFunction): 

2631 if expr.sys2 == tf: 

2632 den_term_2 = Series(*num_arg_list) 

2633 else: 

2634 den_term_2 = tf, Series(*num_arg_list, expr.sys2) 

2635 elif isinstance(num, TransferFunction) and isinstance(expr.sys2, Series): 

2636 if num == tf: 

2637 den_term_2 = Series(*den_arg_list) 

2638 else: 

2639 den_term_2 = Series(num, *den_arg_list) 

2640 else: 

2641 if num == tf: 

2642 den_term_2 = Series(*den_arg_list) 

2643 elif expr.sys2 == tf: 

2644 den_term_2 = Series(*num_arg_list) 

2645 else: 

2646 den_term_2 = Series(*num_arg_list, *den_arg_list) 

2647 

2648 numer = self._print(num) 

2649 denom_1 = self._print(den_term_1) 

2650 denom_2 = self._print(den_term_2) 

2651 _sign = "+" if expr.sign == -1 else "-" 

2652 

2653 return r"\frac{%s}{%s %s %s}" % (numer, denom_1, _sign, denom_2) 

2654 

2655 def _print_MIMOFeedback(self, expr): 

2656 from sympy.physics.control import MIMOSeries 

2657 inv_mat = self._print(MIMOSeries(expr.sys2, expr.sys1)) 

2658 sys1 = self._print(expr.sys1) 

2659 _sign = "+" if expr.sign == -1 else "-" 

2660 return r"\left(I_{\tau} %s %s\right)^{-1} \cdot %s" % (_sign, inv_mat, sys1) 

2661 

2662 def _print_TransferFunctionMatrix(self, expr): 

2663 mat = self._print(expr._expr_mat) 

2664 return r"%s_\tau" % mat 

2665 

2666 def _print_DFT(self, expr): 

2667 return r"\text{{{}}}_{{{}}}".format(expr.__class__.__name__, expr.n) 

2668 _print_IDFT = _print_DFT 

2669 

2670 def _print_NamedMorphism(self, morphism): 

2671 pretty_name = self._print(Symbol(morphism.name)) 

2672 pretty_morphism = self._print_Morphism(morphism) 

2673 return "%s:%s" % (pretty_name, pretty_morphism) 

2674 

2675 def _print_IdentityMorphism(self, morphism): 

2676 from sympy.categories import NamedMorphism 

2677 return self._print_NamedMorphism(NamedMorphism( 

2678 morphism.domain, morphism.codomain, "id")) 

2679 

2680 def _print_CompositeMorphism(self, morphism): 

2681 # All components of the morphism have names and it is thus 

2682 # possible to build the name of the composite. 

2683 component_names_list = [self._print(Symbol(component.name)) for 

2684 component in morphism.components] 

2685 component_names_list.reverse() 

2686 component_names = "\\circ ".join(component_names_list) + ":" 

2687 

2688 pretty_morphism = self._print_Morphism(morphism) 

2689 return component_names + pretty_morphism 

2690 

2691 def _print_Category(self, morphism): 

2692 return r"\mathbf{{{}}}".format(self._print(Symbol(morphism.name))) 

2693 

2694 def _print_Diagram(self, diagram): 

2695 if not diagram.premises: 

2696 # This is an empty diagram. 

2697 return self._print(S.EmptySet) 

2698 

2699 latex_result = self._print(diagram.premises) 

2700 if diagram.conclusions: 

2701 latex_result += "\\Longrightarrow %s" % \ 

2702 self._print(diagram.conclusions) 

2703 

2704 return latex_result 

2705 

2706 def _print_DiagramGrid(self, grid): 

2707 latex_result = "\\begin{array}{%s}\n" % ("c" * grid.width) 

2708 

2709 for i in range(grid.height): 

2710 for j in range(grid.width): 

2711 if grid[i, j]: 

2712 latex_result += latex(grid[i, j]) 

2713 latex_result += " " 

2714 if j != grid.width - 1: 

2715 latex_result += "& " 

2716 

2717 if i != grid.height - 1: 

2718 latex_result += "\\\\" 

2719 latex_result += "\n" 

2720 

2721 latex_result += "\\end{array}\n" 

2722 return latex_result 

2723 

2724 def _print_FreeModule(self, M): 

2725 return '{{{}}}^{{{}}}'.format(self._print(M.ring), self._print(M.rank)) 

2726 

2727 def _print_FreeModuleElement(self, m): 

2728 # Print as row vector for convenience, for now. 

2729 return r"\left[ {} \right]".format(",".join( 

2730 '{' + self._print(x) + '}' for x in m)) 

2731 

2732 def _print_SubModule(self, m): 

2733 return r"\left\langle {} \right\rangle".format(",".join( 

2734 '{' + self._print(x) + '}' for x in m.gens)) 

2735 

2736 def _print_ModuleImplementedIdeal(self, m): 

2737 return r"\left\langle {} \right\rangle".format(",".join( 

2738 '{' + self._print(x) + '}' for [x] in m._module.gens)) 

2739 

2740 def _print_Quaternion(self, expr): 

2741 # TODO: This expression is potentially confusing, 

2742 # shall we print it as `Quaternion( ... )`? 

2743 s = [self.parenthesize(i, PRECEDENCE["Mul"], strict=True) 

2744 for i in expr.args] 

2745 a = [s[0]] + [i+" "+j for i, j in zip(s[1:], "ijk")] 

2746 return " + ".join(a) 

2747 

2748 def _print_QuotientRing(self, R): 

2749 # TODO nicer fractions for few generators... 

2750 return r"\frac{{{}}}{{{}}}".format(self._print(R.ring), 

2751 self._print(R.base_ideal)) 

2752 

2753 def _print_QuotientRingElement(self, x): 

2754 return r"{{{}}} + {{{}}}".format(self._print(x.data), 

2755 self._print(x.ring.base_ideal)) 

2756 

2757 def _print_QuotientModuleElement(self, m): 

2758 return r"{{{}}} + {{{}}}".format(self._print(m.data), 

2759 self._print(m.module.killed_module)) 

2760 

2761 def _print_QuotientModule(self, M): 

2762 # TODO nicer fractions for few generators... 

2763 return r"\frac{{{}}}{{{}}}".format(self._print(M.base), 

2764 self._print(M.killed_module)) 

2765 

2766 def _print_MatrixHomomorphism(self, h): 

2767 return r"{{{}}} : {{{}}} \to {{{}}}".format(self._print(h._sympy_matrix()), 

2768 self._print(h.domain), self._print(h.codomain)) 

2769 

2770 def _print_Manifold(self, manifold): 

2771 string = manifold.name.name 

2772 if '{' in string: 

2773 name, supers, subs = string, [], [] 

2774 else: 

2775 name, supers, subs = split_super_sub(string) 

2776 

2777 name = translate(name) 

2778 supers = [translate(sup) for sup in supers] 

2779 subs = [translate(sub) for sub in subs] 

2780 

2781 name = r'\text{%s}' % name 

2782 if supers: 

2783 name += "^{%s}" % " ".join(supers) 

2784 if subs: 

2785 name += "_{%s}" % " ".join(subs) 

2786 

2787 return name 

2788 

2789 def _print_Patch(self, patch): 

2790 return r'\text{%s}_{%s}' % (self._print(patch.name), self._print(patch.manifold)) 

2791 

2792 def _print_CoordSystem(self, coordsys): 

2793 return r'\text{%s}^{\text{%s}}_{%s}' % ( 

2794 self._print(coordsys.name), self._print(coordsys.patch.name), self._print(coordsys.manifold) 

2795 ) 

2796 

2797 def _print_CovarDerivativeOp(self, cvd): 

2798 return r'\mathbb{\nabla}_{%s}' % self._print(cvd._wrt) 

2799 

2800 def _print_BaseScalarField(self, field): 

2801 string = field._coord_sys.symbols[field._index].name 

2802 return r'\mathbf{{{}}}'.format(self._print(Symbol(string))) 

2803 

2804 def _print_BaseVectorField(self, field): 

2805 string = field._coord_sys.symbols[field._index].name 

2806 return r'\partial_{{{}}}'.format(self._print(Symbol(string))) 

2807 

2808 def _print_Differential(self, diff): 

2809 field = diff._form_field 

2810 if hasattr(field, '_coord_sys'): 

2811 string = field._coord_sys.symbols[field._index].name 

2812 return r'\operatorname{{d}}{}'.format(self._print(Symbol(string))) 

2813 else: 

2814 string = self._print(field) 

2815 return r'\operatorname{{d}}\left({}\right)'.format(string) 

2816 

2817 def _print_Tr(self, p): 

2818 # TODO: Handle indices 

2819 contents = self._print(p.args[0]) 

2820 return r'\operatorname{{tr}}\left({}\right)'.format(contents) 

2821 

2822 def _print_totient(self, expr, exp=None): 

2823 if exp is not None: 

2824 return r'\left(\phi\left(%s\right)\right)^{%s}' % \ 

2825 (self._print(expr.args[0]), exp) 

2826 return r'\phi\left(%s\right)' % self._print(expr.args[0]) 

2827 

2828 def _print_reduced_totient(self, expr, exp=None): 

2829 if exp is not None: 

2830 return r'\left(\lambda\left(%s\right)\right)^{%s}' % \ 

2831 (self._print(expr.args[0]), exp) 

2832 return r'\lambda\left(%s\right)' % self._print(expr.args[0]) 

2833 

2834 def _print_divisor_sigma(self, expr, exp=None): 

2835 if len(expr.args) == 2: 

2836 tex = r"_%s\left(%s\right)" % tuple(map(self._print, 

2837 (expr.args[1], expr.args[0]))) 

2838 else: 

2839 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

2840 if exp is not None: 

2841 return r"\sigma^{%s}%s" % (exp, tex) 

2842 return r"\sigma%s" % tex 

2843 

2844 def _print_udivisor_sigma(self, expr, exp=None): 

2845 if len(expr.args) == 2: 

2846 tex = r"_%s\left(%s\right)" % tuple(map(self._print, 

2847 (expr.args[1], expr.args[0]))) 

2848 else: 

2849 tex = r"\left(%s\right)" % self._print(expr.args[0]) 

2850 if exp is not None: 

2851 return r"\sigma^*^{%s}%s" % (exp, tex) 

2852 return r"\sigma^*%s" % tex 

2853 

2854 def _print_primenu(self, expr, exp=None): 

2855 if exp is not None: 

2856 return r'\left(\nu\left(%s\right)\right)^{%s}' % \ 

2857 (self._print(expr.args[0]), exp) 

2858 return r'\nu\left(%s\right)' % self._print(expr.args[0]) 

2859 

2860 def _print_primeomega(self, expr, exp=None): 

2861 if exp is not None: 

2862 return r'\left(\Omega\left(%s\right)\right)^{%s}' % \ 

2863 (self._print(expr.args[0]), exp) 

2864 return r'\Omega\left(%s\right)' % self._print(expr.args[0]) 

2865 

2866 def _print_Str(self, s): 

2867 return str(s.name) 

2868 

2869 def _print_float(self, expr): 

2870 return self._print(Float(expr)) 

2871 

2872 def _print_int(self, expr): 

2873 return str(expr) 

2874 

2875 def _print_mpz(self, expr): 

2876 return str(expr) 

2877 

2878 def _print_mpq(self, expr): 

2879 return str(expr) 

2880 

2881 def _print_Predicate(self, expr): 

2882 return r"\operatorname{{Q}}_{{\text{{{}}}}}".format(latex_escape(str(expr.name))) 

2883 

2884 def _print_AppliedPredicate(self, expr): 

2885 pred = expr.function 

2886 args = expr.arguments 

2887 pred_latex = self._print(pred) 

2888 args_latex = ', '.join([self._print(a) for a in args]) 

2889 return '%s(%s)' % (pred_latex, args_latex) 

2890 

2891 def emptyPrinter(self, expr): 

2892 # default to just printing as monospace, like would normally be shown 

2893 s = super().emptyPrinter(expr) 

2894 

2895 return r"\mathtt{\text{%s}}" % latex_escape(s) 

2896 

2897 

2898def translate(s: str) -> str: 

2899 r''' 

2900 Check for a modifier ending the string. If present, convert the 

2901 modifier to latex and translate the rest recursively. 

2902 

2903 Given a description of a Greek letter or other special character, 

2904 return the appropriate latex. 

2905 

2906 Let everything else pass as given. 

2907 

2908 >>> from sympy.printing.latex import translate 

2909 >>> translate('alphahatdotprime') 

2910 "{\\dot{\\hat{\\alpha}}}'" 

2911 ''' 

2912 # Process the rest 

2913 tex = tex_greek_dictionary.get(s) 

2914 if tex: 

2915 return tex 

2916 elif s.lower() in greek_letters_set: 

2917 return "\\" + s.lower() 

2918 elif s in other_symbols: 

2919 return "\\" + s 

2920 else: 

2921 # Process modifiers, if any, and recurse 

2922 for key in sorted(modifier_dict.keys(), key=len, reverse=True): 

2923 if s.lower().endswith(key) and len(s) > len(key): 

2924 return modifier_dict[key](translate(s[:-len(key)])) 

2925 return s 

2926 

2927 

2928 

2929@print_function(LatexPrinter) 

2930def latex(expr, **settings): 

2931 r"""Convert the given expression to LaTeX string representation. 

2932 

2933 Parameters 

2934 ========== 

2935 full_prec: boolean, optional 

2936 If set to True, a floating point number is printed with full precision. 

2937 fold_frac_powers : boolean, optional 

2938 Emit ``^{p/q}`` instead of ``^{\frac{p}{q}}`` for fractional powers. 

2939 fold_func_brackets : boolean, optional 

2940 Fold function brackets where applicable. 

2941 fold_short_frac : boolean, optional 

2942 Emit ``p / q`` instead of ``\frac{p}{q}`` when the denominator is 

2943 simple enough (at most two terms and no powers). The default value is 

2944 ``True`` for inline mode, ``False`` otherwise. 

2945 inv_trig_style : string, optional 

2946 How inverse trig functions should be displayed. Can be one of 

2947 ``'abbreviated'``, ``'full'``, or ``'power'``. Defaults to 

2948 ``'abbreviated'``. 

2949 itex : boolean, optional 

2950 Specifies if itex-specific syntax is used, including emitting 

2951 ``$$...$$``. 

2952 ln_notation : boolean, optional 

2953 If set to ``True``, ``\ln`` is used instead of default ``\log``. 

2954 long_frac_ratio : float or None, optional 

2955 The allowed ratio of the width of the numerator to the width of the 

2956 denominator before the printer breaks off long fractions. If ``None`` 

2957 (the default value), long fractions are not broken up. 

2958 mat_delim : string, optional 

2959 The delimiter to wrap around matrices. Can be one of ``'['``, ``'('``, 

2960 or the empty string ``''``. Defaults to ``'['``. 

2961 mat_str : string, optional 

2962 Which matrix environment string to emit. ``'smallmatrix'``, 

2963 ``'matrix'``, ``'array'``, etc. Defaults to ``'smallmatrix'`` for 

2964 inline mode, ``'matrix'`` for matrices of no more than 10 columns, and 

2965 ``'array'`` otherwise. 

2966 mode: string, optional 

2967 Specifies how the generated code will be delimited. ``mode`` can be one 

2968 of ``'plain'``, ``'inline'``, ``'equation'`` or ``'equation*'``. If 

2969 ``mode`` is set to ``'plain'``, then the resulting code will not be 

2970 delimited at all (this is the default). If ``mode`` is set to 

2971 ``'inline'`` then inline LaTeX ``$...$`` will be used. If ``mode`` is 

2972 set to ``'equation'`` or ``'equation*'``, the resulting code will be 

2973 enclosed in the ``equation`` or ``equation*`` environment (remember to 

2974 import ``amsmath`` for ``equation*``), unless the ``itex`` option is 

2975 set. In the latter case, the ``$$...$$`` syntax is used. 

2976 mul_symbol : string or None, optional 

2977 The symbol to use for multiplication. Can be one of ``None``, 

2978 ``'ldot'``, ``'dot'``, or ``'times'``. 

2979 order: string, optional 

2980 Any of the supported monomial orderings (currently ``'lex'``, 

2981 ``'grlex'``, or ``'grevlex'``), ``'old'``, and ``'none'``. This 

2982 parameter does nothing for `~.Mul` objects. Setting order to ``'old'`` 

2983 uses the compatibility ordering for ``~.Add`` defined in Printer. For 

2984 very large expressions, set the ``order`` keyword to ``'none'`` if 

2985 speed is a concern. 

2986 symbol_names : dictionary of strings mapped to symbols, optional 

2987 Dictionary of symbols and the custom strings they should be emitted as. 

2988 root_notation : boolean, optional 

2989 If set to ``False``, exponents of the form 1/n are printed in fractonal 

2990 form. Default is ``True``, to print exponent in root form. 

2991 mat_symbol_style : string, optional 

2992 Can be either ``'plain'`` (default) or ``'bold'``. If set to 

2993 ``'bold'``, a `~.MatrixSymbol` A will be printed as ``\mathbf{A}``, 

2994 otherwise as ``A``. 

2995 imaginary_unit : string, optional 

2996 String to use for the imaginary unit. Defined options are ``'i'`` 

2997 (default) and ``'j'``. Adding ``r`` or ``t`` in front gives ``\mathrm`` 

2998 or ``\text``, so ``'ri'`` leads to ``\mathrm{i}`` which gives 

2999 `\mathrm{i}`. 

3000 gothic_re_im : boolean, optional 

3001 If set to ``True``, `\Re` and `\Im` is used for ``re`` and ``im``, respectively. 

3002 The default is ``False`` leading to `\operatorname{re}` and `\operatorname{im}`. 

3003 decimal_separator : string, optional 

3004 Specifies what separator to use to separate the whole and fractional parts of a 

3005 floating point number as in `2.5` for the default, ``period`` or `2{,}5` 

3006 when ``comma`` is specified. Lists, sets, and tuple are printed with semicolon 

3007 separating the elements when ``comma`` is chosen. For example, [1; 2; 3] when 

3008 ``comma`` is chosen and [1,2,3] for when ``period`` is chosen. 

3009 parenthesize_super : boolean, optional 

3010 If set to ``False``, superscripted expressions will not be parenthesized when 

3011 powered. Default is ``True``, which parenthesizes the expression when powered. 

3012 min: Integer or None, optional 

3013 Sets the lower bound for the exponent to print floating point numbers in 

3014 fixed-point format. 

3015 max: Integer or None, optional 

3016 Sets the upper bound for the exponent to print floating point numbers in 

3017 fixed-point format. 

3018 diff_operator: string, optional 

3019 String to use for differential operator. Default is ``'d'``, to print in italic 

3020 form. ``'rd'``, ``'td'`` are shortcuts for ``\mathrm{d}`` and ``\text{d}``. 

3021 

3022 Notes 

3023 ===== 

3024 

3025 Not using a print statement for printing, results in double backslashes for 

3026 latex commands since that's the way Python escapes backslashes in strings. 

3027 

3028 >>> from sympy import latex, Rational 

3029 >>> from sympy.abc import tau 

3030 >>> latex((2*tau)**Rational(7,2)) 

3031 '8 \\sqrt{2} \\tau^{\\frac{7}{2}}' 

3032 >>> print(latex((2*tau)**Rational(7,2))) 

3033 8 \sqrt{2} \tau^{\frac{7}{2}} 

3034 

3035 Examples 

3036 ======== 

3037 

3038 >>> from sympy import latex, pi, sin, asin, Integral, Matrix, Rational, log 

3039 >>> from sympy.abc import x, y, mu, r, tau 

3040 

3041 Basic usage: 

3042 

3043 >>> print(latex((2*tau)**Rational(7,2))) 

3044 8 \sqrt{2} \tau^{\frac{7}{2}} 

3045 

3046 ``mode`` and ``itex`` options: 

3047 

3048 >>> print(latex((2*mu)**Rational(7,2), mode='plain')) 

3049 8 \sqrt{2} \mu^{\frac{7}{2}} 

3050 >>> print(latex((2*tau)**Rational(7,2), mode='inline')) 

3051 $8 \sqrt{2} \tau^{7 / 2}$ 

3052 >>> print(latex((2*mu)**Rational(7,2), mode='equation*')) 

3053 \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*} 

3054 >>> print(latex((2*mu)**Rational(7,2), mode='equation')) 

3055 \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation} 

3056 >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True)) 

3057 $$8 \sqrt{2} \mu^{\frac{7}{2}}$$ 

3058 >>> print(latex((2*mu)**Rational(7,2), mode='plain')) 

3059 8 \sqrt{2} \mu^{\frac{7}{2}} 

3060 >>> print(latex((2*tau)**Rational(7,2), mode='inline')) 

3061 $8 \sqrt{2} \tau^{7 / 2}$ 

3062 >>> print(latex((2*mu)**Rational(7,2), mode='equation*')) 

3063 \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*} 

3064 >>> print(latex((2*mu)**Rational(7,2), mode='equation')) 

3065 \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation} 

3066 >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True)) 

3067 $$8 \sqrt{2} \mu^{\frac{7}{2}}$$ 

3068 

3069 Fraction options: 

3070 

3071 >>> print(latex((2*tau)**Rational(7,2), fold_frac_powers=True)) 

3072 8 \sqrt{2} \tau^{7/2} 

3073 >>> print(latex((2*tau)**sin(Rational(7,2)))) 

3074 \left(2 \tau\right)^{\sin{\left(\frac{7}{2} \right)}} 

3075 >>> print(latex((2*tau)**sin(Rational(7,2)), fold_func_brackets=True)) 

3076 \left(2 \tau\right)^{\sin {\frac{7}{2}}} 

3077 >>> print(latex(3*x**2/y)) 

3078 \frac{3 x^{2}}{y} 

3079 >>> print(latex(3*x**2/y, fold_short_frac=True)) 

3080 3 x^{2} / y 

3081 >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=2)) 

3082 \frac{\int r\, dr}{2 \pi} 

3083 >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=0)) 

3084 \frac{1}{2 \pi} \int r\, dr 

3085 

3086 Multiplication options: 

3087 

3088 >>> print(latex((2*tau)**sin(Rational(7,2)), mul_symbol="times")) 

3089 \left(2 \times \tau\right)^{\sin{\left(\frac{7}{2} \right)}} 

3090 

3091 Trig options: 

3092 

3093 >>> print(latex(asin(Rational(7,2)))) 

3094 \operatorname{asin}{\left(\frac{7}{2} \right)} 

3095 >>> print(latex(asin(Rational(7,2)), inv_trig_style="full")) 

3096 \arcsin{\left(\frac{7}{2} \right)} 

3097 >>> print(latex(asin(Rational(7,2)), inv_trig_style="power")) 

3098 \sin^{-1}{\left(\frac{7}{2} \right)} 

3099 

3100 Matrix options: 

3101 

3102 >>> print(latex(Matrix(2, 1, [x, y]))) 

3103 \left[\begin{matrix}x\\y\end{matrix}\right] 

3104 >>> print(latex(Matrix(2, 1, [x, y]), mat_str = "array")) 

3105 \left[\begin{array}{c}x\\y\end{array}\right] 

3106 >>> print(latex(Matrix(2, 1, [x, y]), mat_delim="(")) 

3107 \left(\begin{matrix}x\\y\end{matrix}\right) 

3108 

3109 Custom printing of symbols: 

3110 

3111 >>> print(latex(x**2, symbol_names={x: 'x_i'})) 

3112 x_i^{2} 

3113 

3114 Logarithms: 

3115 

3116 >>> print(latex(log(10))) 

3117 \log{\left(10 \right)} 

3118 >>> print(latex(log(10), ln_notation=True)) 

3119 \ln{\left(10 \right)} 

3120 

3121 ``latex()`` also supports the builtin container types :class:`list`, 

3122 :class:`tuple`, and :class:`dict`: 

3123 

3124 >>> print(latex([2/x, y], mode='inline')) 

3125 $\left[ 2 / x, \ y\right]$ 

3126 

3127 Unsupported types are rendered as monospaced plaintext: 

3128 

3129 >>> print(latex(int)) 

3130 \mathtt{\text{<class 'int'>}} 

3131 >>> print(latex("plain % text")) 

3132 \mathtt{\text{plain \% text}} 

3133 

3134 See :ref:`printer_method_example` for an example of how to override 

3135 this behavior for your own types by implementing ``_latex``. 

3136 

3137 .. versionchanged:: 1.7.0 

3138 Unsupported types no longer have their ``str`` representation treated as valid latex. 

3139 

3140 """ 

3141 return LatexPrinter(settings).doprint(expr) 

3142 

3143 

3144def print_latex(expr, **settings): 

3145 """Prints LaTeX representation of the given expression. Takes the same 

3146 settings as ``latex()``.""" 

3147 

3148 print(latex(expr, **settings)) 

3149 

3150 

3151def multiline_latex(lhs, rhs, terms_per_line=1, environment="align*", use_dots=False, **settings): 

3152 r""" 

3153 This function generates a LaTeX equation with a multiline right-hand side 

3154 in an ``align*``, ``eqnarray`` or ``IEEEeqnarray`` environment. 

3155 

3156 Parameters 

3157 ========== 

3158 

3159 lhs : Expr 

3160 Left-hand side of equation 

3161 

3162 rhs : Expr 

3163 Right-hand side of equation 

3164 

3165 terms_per_line : integer, optional 

3166 Number of terms per line to print. Default is 1. 

3167 

3168 environment : "string", optional 

3169 Which LaTeX wnvironment to use for the output. Options are "align*" 

3170 (default), "eqnarray", and "IEEEeqnarray". 

3171 

3172 use_dots : boolean, optional 

3173 If ``True``, ``\\dots`` is added to the end of each line. Default is ``False``. 

3174 

3175 Examples 

3176 ======== 

3177 

3178 >>> from sympy import multiline_latex, symbols, sin, cos, exp, log, I 

3179 >>> x, y, alpha = symbols('x y alpha') 

3180 >>> expr = sin(alpha*y) + exp(I*alpha) - cos(log(y)) 

3181 >>> print(multiline_latex(x, expr)) 

3182 \begin{align*} 

3183 x = & e^{i \alpha} \\ 

3184 & + \sin{\left(\alpha y \right)} \\ 

3185 & - \cos{\left(\log{\left(y \right)} \right)} 

3186 \end{align*} 

3187 

3188 Using at most two terms per line: 

3189 >>> print(multiline_latex(x, expr, 2)) 

3190 \begin{align*} 

3191 x = & e^{i \alpha} + \sin{\left(\alpha y \right)} \\ 

3192 & - \cos{\left(\log{\left(y \right)} \right)} 

3193 \end{align*} 

3194 

3195 Using ``eqnarray`` and dots: 

3196 >>> print(multiline_latex(x, expr, terms_per_line=2, environment="eqnarray", use_dots=True)) 

3197 \begin{eqnarray} 

3198 x & = & e^{i \alpha} + \sin{\left(\alpha y \right)} \dots\nonumber\\ 

3199 & & - \cos{\left(\log{\left(y \right)} \right)} 

3200 \end{eqnarray} 

3201 

3202 Using ``IEEEeqnarray``: 

3203 >>> print(multiline_latex(x, expr, environment="IEEEeqnarray")) 

3204 \begin{IEEEeqnarray}{rCl} 

3205 x & = & e^{i \alpha} \nonumber\\ 

3206 & & + \sin{\left(\alpha y \right)} \nonumber\\ 

3207 & & - \cos{\left(\log{\left(y \right)} \right)} 

3208 \end{IEEEeqnarray} 

3209 

3210 Notes 

3211 ===== 

3212 

3213 All optional parameters from ``latex`` can also be used. 

3214 

3215 """ 

3216 

3217 # Based on code from https://github.com/sympy/sympy/issues/3001 

3218 l = LatexPrinter(**settings) 

3219 if environment == "eqnarray": 

3220 result = r'\begin{eqnarray}' + '\n' 

3221 first_term = '& = &' 

3222 nonumber = r'\nonumber' 

3223 end_term = '\n\\end{eqnarray}' 

3224 doubleet = True 

3225 elif environment == "IEEEeqnarray": 

3226 result = r'\begin{IEEEeqnarray}{rCl}' + '\n' 

3227 first_term = '& = &' 

3228 nonumber = r'\nonumber' 

3229 end_term = '\n\\end{IEEEeqnarray}' 

3230 doubleet = True 

3231 elif environment == "align*": 

3232 result = r'\begin{align*}' + '\n' 

3233 first_term = '= &' 

3234 nonumber = '' 

3235 end_term = '\n\\end{align*}' 

3236 doubleet = False 

3237 else: 

3238 raise ValueError("Unknown environment: {}".format(environment)) 

3239 dots = '' 

3240 if use_dots: 

3241 dots=r'\dots' 

3242 terms = rhs.as_ordered_terms() 

3243 n_terms = len(terms) 

3244 term_count = 1 

3245 for i in range(n_terms): 

3246 term = terms[i] 

3247 term_start = '' 

3248 term_end = '' 

3249 sign = '+' 

3250 if term_count > terms_per_line: 

3251 if doubleet: 

3252 term_start = '& & ' 

3253 else: 

3254 term_start = '& ' 

3255 term_count = 1 

3256 if term_count == terms_per_line: 

3257 # End of line 

3258 if i < n_terms-1: 

3259 # There are terms remaining 

3260 term_end = dots + nonumber + r'\\' + '\n' 

3261 else: 

3262 term_end = '' 

3263 

3264 if term.as_ordered_factors()[0] == -1: 

3265 term = -1*term 

3266 sign = r'-' 

3267 if i == 0: # beginning 

3268 if sign == '+': 

3269 sign = '' 

3270 result += r'{:s} {:s}{:s} {:s} {:s}'.format(l.doprint(lhs), 

3271 first_term, sign, l.doprint(term), term_end) 

3272 else: 

3273 result += r'{:s}{:s} {:s} {:s}'.format(term_start, sign, 

3274 l.doprint(term), term_end) 

3275 term_count += 1 

3276 result += end_term 

3277 return result