1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277 |
- """
- A Printer which converts an expression into its LaTeX equivalent.
- """
- from __future__ import annotations
- from typing import Any, Callable, TYPE_CHECKING
- import itertools
- from sympy.core import Add, Float, Mod, Mul, Number, S, Symbol, Expr
- from sympy.core.alphabets import greeks
- from sympy.core.containers import Tuple
- from sympy.core.function import Function, AppliedUndef, Derivative
- from sympy.core.operations import AssocOp
- from sympy.core.power import Pow
- from sympy.core.sorting import default_sort_key
- from sympy.core.sympify import SympifyError
- from sympy.logic.boolalg import true, BooleanTrue, BooleanFalse
- from sympy.tensor.array import NDimArray
- # sympy.printing imports
- from sympy.printing.precedence import precedence_traditional
- from sympy.printing.printer import Printer, print_function
- from sympy.printing.conventions import split_super_sub, requires_partial
- from sympy.printing.precedence import precedence, PRECEDENCE
- from mpmath.libmp.libmpf import prec_to_dps, to_str as mlib_to_str
- from sympy.utilities.iterables import has_variety, sift
- import re
- if TYPE_CHECKING:
- from sympy.vector.basisdependent import BasisDependent
- # Hand-picked functions which can be used directly in both LaTeX and MathJax
- # Complete list at
- # https://docs.mathjax.org/en/latest/tex.html#supported-latex-commands
- # This variable only contains those functions which SymPy uses.
- accepted_latex_functions = ['arcsin', 'arccos', 'arctan', 'sin', 'cos', 'tan',
- 'sinh', 'cosh', 'tanh', 'sqrt', 'ln', 'log', 'sec',
- 'csc', 'cot', 'coth', 're', 'im', 'frac', 'root',
- 'arg',
- ]
- tex_greek_dictionary = {
- 'Alpha': r'\mathrm{A}',
- 'Beta': r'\mathrm{B}',
- 'Gamma': r'\Gamma',
- 'Delta': r'\Delta',
- 'Epsilon': r'\mathrm{E}',
- 'Zeta': r'\mathrm{Z}',
- 'Eta': r'\mathrm{H}',
- 'Theta': r'\Theta',
- 'Iota': r'\mathrm{I}',
- 'Kappa': r'\mathrm{K}',
- 'Lambda': r'\Lambda',
- 'Mu': r'\mathrm{M}',
- 'Nu': r'\mathrm{N}',
- 'Xi': r'\Xi',
- 'omicron': 'o',
- 'Omicron': r'\mathrm{O}',
- 'Pi': r'\Pi',
- 'Rho': r'\mathrm{P}',
- 'Sigma': r'\Sigma',
- 'Tau': r'\mathrm{T}',
- 'Upsilon': r'\Upsilon',
- 'Phi': r'\Phi',
- 'Chi': r'\mathrm{X}',
- 'Psi': r'\Psi',
- 'Omega': r'\Omega',
- 'lamda': r'\lambda',
- 'Lamda': r'\Lambda',
- 'khi': r'\chi',
- 'Khi': r'\mathrm{X}',
- 'varepsilon': r'\varepsilon',
- 'varkappa': r'\varkappa',
- 'varphi': r'\varphi',
- 'varpi': r'\varpi',
- 'varrho': r'\varrho',
- 'varsigma': r'\varsigma',
- 'vartheta': r'\vartheta',
- }
- other_symbols = {'aleph', 'beth', 'daleth', 'gimel', 'ell', 'eth', 'hbar',
- 'hslash', 'mho', 'wp'}
- # Variable name modifiers
- modifier_dict: dict[str, Callable[[str], str]] = {
- # Accents
- 'mathring': lambda s: r'\mathring{'+s+r'}',
- 'ddddot': lambda s: r'\ddddot{'+s+r'}',
- 'dddot': lambda s: r'\dddot{'+s+r'}',
- 'ddot': lambda s: r'\ddot{'+s+r'}',
- 'dot': lambda s: r'\dot{'+s+r'}',
- 'check': lambda s: r'\check{'+s+r'}',
- 'breve': lambda s: r'\breve{'+s+r'}',
- 'acute': lambda s: r'\acute{'+s+r'}',
- 'grave': lambda s: r'\grave{'+s+r'}',
- 'tilde': lambda s: r'\tilde{'+s+r'}',
- 'hat': lambda s: r'\hat{'+s+r'}',
- 'bar': lambda s: r'\bar{'+s+r'}',
- 'vec': lambda s: r'\vec{'+s+r'}',
- 'prime': lambda s: "{"+s+"}'",
- 'prm': lambda s: "{"+s+"}'",
- # Faces
- 'bold': lambda s: r'\boldsymbol{'+s+r'}',
- 'bm': lambda s: r'\boldsymbol{'+s+r'}',
- 'cal': lambda s: r'\mathcal{'+s+r'}',
- 'scr': lambda s: r'\mathscr{'+s+r'}',
- 'frak': lambda s: r'\mathfrak{'+s+r'}',
- # Brackets
- 'norm': lambda s: r'\left\|{'+s+r'}\right\|',
- 'avg': lambda s: r'\left\langle{'+s+r'}\right\rangle',
- 'abs': lambda s: r'\left|{'+s+r'}\right|',
- 'mag': lambda s: r'\left|{'+s+r'}\right|',
- }
- greek_letters_set = frozenset(greeks)
- _between_two_numbers_p = (
- re.compile(r'[0-9][} ]*$'), # search
- re.compile(r'[0-9]'), # match
- )
- def latex_escape(s: str) -> str:
- """
- Escape a string such that latex interprets it as plaintext.
- We cannot use verbatim easily with mathjax, so escaping is easier.
- Rules from https://tex.stackexchange.com/a/34586/41112.
- """
- s = s.replace('\\', r'\textbackslash')
- for c in '&%$#_{}':
- s = s.replace(c, '\\' + c)
- s = s.replace('~', r'\textasciitilde')
- s = s.replace('^', r'\textasciicircum')
- return s
- class LatexPrinter(Printer):
- printmethod = "_latex"
- _default_settings: dict[str, Any] = {
- "full_prec": False,
- "fold_frac_powers": False,
- "fold_func_brackets": False,
- "fold_short_frac": None,
- "inv_trig_style": "abbreviated",
- "itex": False,
- "ln_notation": False,
- "long_frac_ratio": None,
- "mat_delim": "[",
- "mat_str": None,
- "mode": "plain",
- "mul_symbol": None,
- "order": None,
- "symbol_names": {},
- "root_notation": True,
- "mat_symbol_style": "plain",
- "imaginary_unit": "i",
- "gothic_re_im": False,
- "decimal_separator": "period",
- "perm_cyclic": True,
- "parenthesize_super": True,
- "min": None,
- "max": None,
- "diff_operator": "d",
- }
- def __init__(self, settings=None):
- Printer.__init__(self, settings)
- if 'mode' in self._settings:
- valid_modes = ['inline', 'plain', 'equation',
- 'equation*']
- if self._settings['mode'] not in valid_modes:
- raise ValueError("'mode' must be one of 'inline', 'plain', "
- "'equation' or 'equation*'")
- if self._settings['fold_short_frac'] is None and \
- self._settings['mode'] == 'inline':
- self._settings['fold_short_frac'] = True
- mul_symbol_table = {
- None: r" ",
- "ldot": r" \,.\, ",
- "dot": r" \cdot ",
- "times": r" \times "
- }
- try:
- self._settings['mul_symbol_latex'] = \
- mul_symbol_table[self._settings['mul_symbol']]
- except KeyError:
- self._settings['mul_symbol_latex'] = \
- self._settings['mul_symbol']
- try:
- self._settings['mul_symbol_latex_numbers'] = \
- mul_symbol_table[self._settings['mul_symbol'] or 'dot']
- except KeyError:
- if (self._settings['mul_symbol'].strip() in
- ['', ' ', '\\', '\\,', '\\:', '\\;', '\\quad']):
- self._settings['mul_symbol_latex_numbers'] = \
- mul_symbol_table['dot']
- else:
- self._settings['mul_symbol_latex_numbers'] = \
- self._settings['mul_symbol']
- self._delim_dict = {'(': ')', '[': ']'}
- imaginary_unit_table = {
- None: r"i",
- "i": r"i",
- "ri": r"\mathrm{i}",
- "ti": r"\text{i}",
- "j": r"j",
- "rj": r"\mathrm{j}",
- "tj": r"\text{j}",
- }
- imag_unit = self._settings['imaginary_unit']
- self._settings['imaginary_unit_latex'] = imaginary_unit_table.get(imag_unit, imag_unit)
- diff_operator_table = {
- None: r"d",
- "d": r"d",
- "rd": r"\mathrm{d}",
- "td": r"\text{d}",
- }
- diff_operator = self._settings['diff_operator']
- self._settings["diff_operator_latex"] = diff_operator_table.get(diff_operator, diff_operator)
- def _add_parens(self, s) -> str:
- return r"\left({}\right)".format(s)
- # TODO: merge this with the above, which requires a lot of test changes
- def _add_parens_lspace(self, s) -> str:
- return r"\left( {}\right)".format(s)
- def parenthesize(self, item, level, is_neg=False, strict=False) -> str:
- prec_val = precedence_traditional(item)
- if is_neg and strict:
- return self._add_parens(self._print(item))
- if (prec_val < level) or ((not strict) and prec_val <= level):
- return self._add_parens(self._print(item))
- else:
- return self._print(item)
- def parenthesize_super(self, s):
- """
- Protect superscripts in s
- If the parenthesize_super option is set, protect with parentheses, else
- wrap in braces.
- """
- if "^" in s:
- if self._settings['parenthesize_super']:
- return self._add_parens(s)
- else:
- return "{{{}}}".format(s)
- return s
- def doprint(self, expr) -> str:
- tex = Printer.doprint(self, expr)
- if self._settings['mode'] == 'plain':
- return tex
- elif self._settings['mode'] == 'inline':
- return r"$%s$" % tex
- elif self._settings['itex']:
- return r"$$%s$$" % tex
- else:
- env_str = self._settings['mode']
- return r"\begin{%s}%s\end{%s}" % (env_str, tex, env_str)
- def _needs_brackets(self, expr) -> bool:
- """
- Returns True if the expression needs to be wrapped in brackets when
- printed, False otherwise. For example: a + b => True; a => False;
- 10 => False; -10 => True.
- """
- return not ((expr.is_Integer and expr.is_nonnegative)
- or (expr.is_Atom and (expr is not S.NegativeOne
- and expr.is_Rational is False)))
- def _needs_function_brackets(self, expr) -> bool:
- """
- Returns True if the expression needs to be wrapped in brackets when
- passed as an argument to a function, False otherwise. This is a more
- liberal version of _needs_brackets, in that many expressions which need
- to be wrapped in brackets when added/subtracted/raised to a power do
- not need them when passed to a function. Such an example is a*b.
- """
- if not self._needs_brackets(expr):
- return False
- else:
- # Muls of the form a*b*c... can be folded
- if expr.is_Mul and not self._mul_is_clean(expr):
- return True
- # Pows which don't need brackets can be folded
- elif expr.is_Pow and not self._pow_is_clean(expr):
- return True
- # Add and Function always need brackets
- elif expr.is_Add or expr.is_Function:
- return True
- else:
- return False
- def _needs_mul_brackets(self, expr, first=False, last=False) -> bool:
- """
- Returns True if the expression needs to be wrapped in brackets when
- printed as part of a Mul, False otherwise. This is True for Add,
- but also for some container objects that would not need brackets
- when appearing last in a Mul, e.g. an Integral. ``last=True``
- specifies that this expr is the last to appear in a Mul.
- ``first=True`` specifies that this expr is the first to appear in
- a Mul.
- """
- from sympy.concrete.products import Product
- from sympy.concrete.summations import Sum
- from sympy.integrals.integrals import Integral
- if expr.is_Mul:
- if not first and expr.could_extract_minus_sign():
- return True
- elif precedence_traditional(expr) < PRECEDENCE["Mul"]:
- return True
- elif expr.is_Relational:
- return True
- if expr.is_Piecewise:
- return True
- if any(expr.has(x) for x in (Mod,)):
- return True
- if (not last and
- any(expr.has(x) for x in (Integral, Product, Sum))):
- return True
- return False
- def _needs_add_brackets(self, expr) -> bool:
- """
- Returns True if the expression needs to be wrapped in brackets when
- printed as part of an Add, False otherwise. This is False for most
- things.
- """
- if expr.is_Relational:
- return True
- if any(expr.has(x) for x in (Mod,)):
- return True
- if expr.is_Add:
- return True
- return False
- def _mul_is_clean(self, expr) -> bool:
- for arg in expr.args:
- if arg.is_Function:
- return False
- return True
- def _pow_is_clean(self, expr) -> bool:
- return not self._needs_brackets(expr.base)
- def _do_exponent(self, expr: str, exp):
- if exp is not None:
- return r"\left(%s\right)^{%s}" % (expr, exp)
- else:
- return expr
- def _print_Basic(self, expr):
- name = self._deal_with_super_sub(expr.__class__.__name__)
- if expr.args:
- ls = [self._print(o) for o in expr.args]
- s = r"\operatorname{{{}}}\left({}\right)"
- return s.format(name, ", ".join(ls))
- else:
- return r"\text{{{}}}".format(name)
- def _print_bool(self, e: bool | BooleanTrue | BooleanFalse):
- return r"\text{%s}" % e
- _print_BooleanTrue = _print_bool
- _print_BooleanFalse = _print_bool
- def _print_NoneType(self, e):
- return r"\text{%s}" % e
- def _print_Add(self, expr, order=None):
- terms = self._as_ordered_terms(expr, order=order)
- tex = ""
- for i, term in enumerate(terms):
- if i == 0:
- pass
- elif term.could_extract_minus_sign():
- tex += " - "
- term = -term
- else:
- tex += " + "
- term_tex = self._print(term)
- if self._needs_add_brackets(term):
- term_tex = r"\left(%s\right)" % term_tex
- tex += term_tex
- return tex
- def _print_Cycle(self, expr):
- from sympy.combinatorics.permutations import Permutation
- if expr.size == 0:
- return r"\left( \right)"
- expr = Permutation(expr)
- expr_perm = expr.cyclic_form
- siz = expr.size
- if expr.array_form[-1] == siz - 1:
- expr_perm = expr_perm + [[siz - 1]]
- term_tex = ''
- for i in expr_perm:
- term_tex += str(i).replace(',', r"\;")
- term_tex = term_tex.replace('[', r"\left( ")
- term_tex = term_tex.replace(']', r"\right)")
- return term_tex
- def _print_Permutation(self, expr):
- from sympy.combinatorics.permutations import Permutation
- from sympy.utilities.exceptions import sympy_deprecation_warning
- perm_cyclic = Permutation.print_cyclic
- if perm_cyclic is not None:
- sympy_deprecation_warning(
- f"""
- Setting Permutation.print_cyclic is deprecated. Instead use
- init_printing(perm_cyclic={perm_cyclic}).
- """,
- deprecated_since_version="1.6",
- active_deprecations_target="deprecated-permutation-print_cyclic",
- stacklevel=8,
- )
- else:
- perm_cyclic = self._settings.get("perm_cyclic", True)
- if perm_cyclic:
- return self._print_Cycle(expr)
- if expr.size == 0:
- return r"\left( \right)"
- lower = [self._print(arg) for arg in expr.array_form]
- upper = [self._print(arg) for arg in range(len(lower))]
- row1 = " & ".join(upper)
- row2 = " & ".join(lower)
- mat = r" \\ ".join((row1, row2))
- return r"\begin{pmatrix} %s \end{pmatrix}" % mat
- def _print_AppliedPermutation(self, expr):
- perm, var = expr.args
- return r"\sigma_{%s}(%s)" % (self._print(perm), self._print(var))
- def _print_Float(self, expr):
- # Based off of that in StrPrinter
- dps = prec_to_dps(expr._prec)
- strip = False if self._settings['full_prec'] else True
- low = self._settings["min"] if "min" in self._settings else None
- high = self._settings["max"] if "max" in self._settings else None
- str_real = mlib_to_str(expr._mpf_, dps, strip_zeros=strip, min_fixed=low, max_fixed=high)
- # Must always have a mul symbol (as 2.5 10^{20} just looks odd)
- # thus we use the number separator
- separator = self._settings['mul_symbol_latex_numbers']
- if 'e' in str_real:
- (mant, exp) = str_real.split('e')
- if exp[0] == '+':
- exp = exp[1:]
- if self._settings['decimal_separator'] == 'comma':
- mant = mant.replace('.','{,}')
- return r"%s%s10^{%s}" % (mant, separator, exp)
- elif str_real == "+inf":
- return r"\infty"
- elif str_real == "-inf":
- return r"- \infty"
- else:
- if self._settings['decimal_separator'] == 'comma':
- str_real = str_real.replace('.','{,}')
- return str_real
- def _print_Cross(self, expr):
- vec1 = expr._expr1
- vec2 = expr._expr2
- return r"%s \times %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']),
- self.parenthesize(vec2, PRECEDENCE['Mul']))
- def _print_Curl(self, expr):
- vec = expr._expr
- return r"\nabla\times %s" % self.parenthesize(vec, PRECEDENCE['Mul'])
- def _print_Divergence(self, expr):
- vec = expr._expr
- return r"\nabla\cdot %s" % self.parenthesize(vec, PRECEDENCE['Mul'])
- def _print_Dot(self, expr):
- vec1 = expr._expr1
- vec2 = expr._expr2
- return r"%s \cdot %s" % (self.parenthesize(vec1, PRECEDENCE['Mul']),
- self.parenthesize(vec2, PRECEDENCE['Mul']))
- def _print_Gradient(self, expr):
- func = expr._expr
- return r"\nabla %s" % self.parenthesize(func, PRECEDENCE['Mul'])
- def _print_Laplacian(self, expr):
- func = expr._expr
- return r"\Delta %s" % self.parenthesize(func, PRECEDENCE['Mul'])
- def _print_Mul(self, expr: Expr):
- from sympy.simplify import fraction
- separator: str = self._settings['mul_symbol_latex']
- numbersep: str = self._settings['mul_symbol_latex_numbers']
- def convert(expr) -> str:
- if not expr.is_Mul:
- return str(self._print(expr))
- else:
- if self.order not in ('old', 'none'):
- args = expr.as_ordered_factors()
- else:
- args = list(expr.args)
- # If there are quantities or prefixes, append them at the back.
- units, nonunits = sift(args, lambda x: (hasattr(x, "_scale_factor") or hasattr(x, "is_physical_constant")) or
- (isinstance(x, Pow) and
- hasattr(x.base, "is_physical_constant")), binary=True)
- prefixes, units = sift(units, lambda x: hasattr(x, "_scale_factor"), binary=True)
- return convert_args(nonunits + prefixes + units)
- def convert_args(args) -> str:
- _tex = last_term_tex = ""
- for i, term in enumerate(args):
- term_tex = self._print(term)
- if not (hasattr(term, "_scale_factor") or hasattr(term, "is_physical_constant")):
- if self._needs_mul_brackets(term, first=(i == 0),
- last=(i == len(args) - 1)):
- term_tex = r"\left(%s\right)" % term_tex
- if _between_two_numbers_p[0].search(last_term_tex) and \
- _between_two_numbers_p[1].match(str(term)):
- # between two numbers
- _tex += numbersep
- elif _tex:
- _tex += separator
- elif _tex:
- _tex += separator
- _tex += term_tex
- last_term_tex = term_tex
- return _tex
- # Check for unevaluated Mul. In this case we need to make sure the
- # identities are visible, multiple Rational factors are not combined
- # etc so we display in a straight-forward form that fully preserves all
- # args and their order.
- # XXX: _print_Pow calls this routine with instances of Pow...
- if isinstance(expr, Mul):
- args = expr.args
- if args[0] is S.One or any(isinstance(arg, Number) for arg in args[1:]):
- return convert_args(args)
- include_parens = False
- if expr.could_extract_minus_sign():
- expr = -expr
- tex = "- "
- if expr.is_Add:
- tex += "("
- include_parens = True
- else:
- tex = ""
- numer, denom = fraction(expr, exact=True)
- if denom is S.One and Pow(1, -1, evaluate=False) not in expr.args:
- # use the original expression here, since fraction() may have
- # altered it when producing numer and denom
- tex += convert(expr)
- else:
- snumer = convert(numer)
- sdenom = convert(denom)
- ldenom = len(sdenom.split())
- ratio = self._settings['long_frac_ratio']
- if self._settings['fold_short_frac'] and ldenom <= 2 and \
- "^" not in sdenom:
- # handle short fractions
- if self._needs_mul_brackets(numer, last=False):
- tex += r"\left(%s\right) / %s" % (snumer, sdenom)
- else:
- tex += r"%s / %s" % (snumer, sdenom)
- elif ratio is not None and \
- len(snumer.split()) > ratio*ldenom:
- # handle long fractions
- if self._needs_mul_brackets(numer, last=True):
- tex += r"\frac{1}{%s}%s\left(%s\right)" \
- % (sdenom, separator, snumer)
- elif numer.is_Mul:
- # split a long numerator
- a = S.One
- b = S.One
- for x in numer.args:
- if self._needs_mul_brackets(x, last=False) or \
- len(convert(a*x).split()) > ratio*ldenom or \
- (b.is_commutative is x.is_commutative is False):
- b *= x
- else:
- a *= x
- if self._needs_mul_brackets(b, last=True):
- tex += r"\frac{%s}{%s}%s\left(%s\right)" \
- % (convert(a), sdenom, separator, convert(b))
- else:
- tex += r"\frac{%s}{%s}%s%s" \
- % (convert(a), sdenom, separator, convert(b))
- else:
- tex += r"\frac{1}{%s}%s%s" % (sdenom, separator, snumer)
- else:
- tex += r"\frac{%s}{%s}" % (snumer, sdenom)
- if include_parens:
- tex += ")"
- return tex
- def _print_AlgebraicNumber(self, expr):
- if expr.is_aliased:
- return self._print(expr.as_poly().as_expr())
- else:
- return self._print(expr.as_expr())
- def _print_PrimeIdeal(self, expr):
- p = self._print(expr.p)
- if expr.is_inert:
- return rf'\left({p}\right)'
- alpha = self._print(expr.alpha.as_expr())
- return rf'\left({p}, {alpha}\right)'
- def _print_Pow(self, expr: Pow):
- # Treat x**Rational(1,n) as special case
- if expr.exp.is_Rational:
- p: int = expr.exp.p # type: ignore
- q: int = expr.exp.q # type: ignore
- if abs(p) == 1 and q != 1 and self._settings['root_notation']:
- base = self._print(expr.base)
- if q == 2:
- tex = r"\sqrt{%s}" % base
- elif self._settings['itex']:
- tex = r"\root{%d}{%s}" % (q, base)
- else:
- tex = r"\sqrt[%d]{%s}" % (q, base)
- if expr.exp.is_negative:
- return r"\frac{1}{%s}" % tex
- else:
- return tex
- elif self._settings['fold_frac_powers'] and q != 1:
- base = self.parenthesize(expr.base, PRECEDENCE['Pow'])
- # issue #12886: add parentheses for superscripts raised to powers
- if expr.base.is_Symbol:
- base = self.parenthesize_super(base)
- if expr.base.is_Function:
- return self._print(expr.base, exp="%s/%s" % (p, q))
- return r"%s^{%s/%s}" % (base, p, q)
- elif expr.exp.is_negative and expr.base.is_commutative:
- # special case for 1^(-x), issue 9216
- if expr.base == 1:
- return r"%s^{%s}" % (expr.base, expr.exp)
- # special case for (1/x)^(-y) and (-1/-x)^(-y), issue 20252
- if expr.base.is_Rational:
- base_p: int = expr.base.p # type: ignore
- base_q: int = expr.base.q # type: ignore
- if base_p * base_q == abs(base_q):
- if expr.exp == -1:
- return r"\frac{1}{\frac{%s}{%s}}" % (base_p, base_q)
- else:
- return r"\frac{1}{(\frac{%s}{%s})^{%s}}" % (base_p, base_q, abs(expr.exp))
- # things like 1/x
- return self._print_Mul(expr)
- if expr.base.is_Function:
- return self._print(expr.base, exp=self._print(expr.exp))
- tex = r"%s^{%s}"
- return self._helper_print_standard_power(expr, tex)
- def _helper_print_standard_power(self, expr, template: str) -> str:
- exp = self._print(expr.exp)
- # issue #12886: add parentheses around superscripts raised
- # to powers
- base = self.parenthesize(expr.base, PRECEDENCE['Pow'])
- if expr.base.is_Symbol:
- base = self.parenthesize_super(base)
- elif (isinstance(expr.base, Derivative)
- and base.startswith(r'\left(')
- and re.match(r'\\left\(\\d?d?dot', base)
- and base.endswith(r'\right)')):
- # don't use parentheses around dotted derivative
- base = base[6: -7] # remove outermost added parens
- return template % (base, exp)
- def _print_UnevaluatedExpr(self, expr):
- return self._print(expr.args[0])
- def _print_Sum(self, expr):
- if len(expr.limits) == 1:
- tex = r"\sum_{%s=%s}^{%s} " % \
- tuple([self._print(i) for i in expr.limits[0]])
- else:
- def _format_ineq(l):
- return r"%s \leq %s \leq %s" % \
- tuple([self._print(s) for s in (l[1], l[0], l[2])])
- tex = r"\sum_{\substack{%s}} " % \
- str.join('\\\\', [_format_ineq(l) for l in expr.limits])
- if isinstance(expr.function, Add):
- tex += r"\left(%s\right)" % self._print(expr.function)
- else:
- tex += self._print(expr.function)
- return tex
- def _print_Product(self, expr):
- if len(expr.limits) == 1:
- tex = r"\prod_{%s=%s}^{%s} " % \
- tuple([self._print(i) for i in expr.limits[0]])
- else:
- def _format_ineq(l):
- return r"%s \leq %s \leq %s" % \
- tuple([self._print(s) for s in (l[1], l[0], l[2])])
- tex = r"\prod_{\substack{%s}} " % \
- str.join('\\\\', [_format_ineq(l) for l in expr.limits])
- if isinstance(expr.function, Add):
- tex += r"\left(%s\right)" % self._print(expr.function)
- else:
- tex += self._print(expr.function)
- return tex
- def _print_BasisDependent(self, expr: 'BasisDependent'):
- from sympy.vector import Vector
- o1: list[str] = []
- if expr == expr.zero:
- return expr.zero._latex_form
- if isinstance(expr, Vector):
- items = expr.separate().items()
- else:
- items = [(0, expr)]
- for system, vect in items:
- inneritems = list(vect.components.items())
- inneritems.sort(key=lambda x: x[0].__str__())
- for k, v in inneritems:
- if v == 1:
- o1.append(' + ' + k._latex_form)
- elif v == -1:
- o1.append(' - ' + k._latex_form)
- else:
- arg_str = r'\left(' + self._print(v) + r'\right)'
- o1.append(' + ' + arg_str + k._latex_form)
- outstr = (''.join(o1))
- if outstr[1] != '-':
- outstr = outstr[3:]
- else:
- outstr = outstr[1:]
- return outstr
- def _print_Indexed(self, expr):
- tex_base = self._print(expr.base)
- tex = '{'+tex_base+'}'+'_{%s}' % ','.join(
- map(self._print, expr.indices))
- return tex
- def _print_IndexedBase(self, expr):
- return self._print(expr.label)
- def _print_Idx(self, expr):
- label = self._print(expr.label)
- if expr.upper is not None:
- upper = self._print(expr.upper)
- if expr.lower is not None:
- lower = self._print(expr.lower)
- else:
- lower = self._print(S.Zero)
- interval = '{lower}\\mathrel{{..}}\\nobreak {upper}'.format(
- lower = lower, upper = upper)
- return '{{{label}}}_{{{interval}}}'.format(
- label = label, interval = interval)
- #if no bounds are defined this just prints the label
- return label
- def _print_Derivative(self, expr):
- if requires_partial(expr.expr):
- diff_symbol = r'\partial'
- else:
- diff_symbol = self._settings["diff_operator_latex"]
- tex = ""
- dim = 0
- for x, num in reversed(expr.variable_count):
- dim += num
- if num == 1:
- tex += r"%s %s" % (diff_symbol, self._print(x))
- else:
- tex += r"%s %s^{%s}" % (diff_symbol,
- self.parenthesize_super(self._print(x)),
- self._print(num))
- if dim == 1:
- tex = r"\frac{%s}{%s}" % (diff_symbol, tex)
- else:
- tex = r"\frac{%s^{%s}}{%s}" % (diff_symbol, self._print(dim), tex)
- if any(i.could_extract_minus_sign() for i in expr.args):
- return r"%s %s" % (tex, self.parenthesize(expr.expr,
- PRECEDENCE["Mul"],
- is_neg=True,
- strict=True))
- return r"%s %s" % (tex, self.parenthesize(expr.expr,
- PRECEDENCE["Mul"],
- is_neg=False,
- strict=True))
- def _print_Subs(self, subs):
- expr, old, new = subs.args
- latex_expr = self._print(expr)
- latex_old = (self._print(e) for e in old)
- latex_new = (self._print(e) for e in new)
- latex_subs = r'\\ '.join(
- e[0] + '=' + e[1] for e in zip(latex_old, latex_new))
- return r'\left. %s \right|_{\substack{ %s }}' % (latex_expr,
- latex_subs)
- def _print_Integral(self, expr):
- tex, symbols = "", []
- diff_symbol = self._settings["diff_operator_latex"]
- # Only up to \iiiint exists
- if len(expr.limits) <= 4 and all(len(lim) == 1 for lim in expr.limits):
- # Use len(expr.limits)-1 so that syntax highlighters don't think
- # \" is an escaped quote
- tex = r"\i" + "i"*(len(expr.limits) - 1) + "nt"
- symbols = [r"\, %s%s" % (diff_symbol, self._print(symbol[0]))
- for symbol in expr.limits]
- else:
- for lim in reversed(expr.limits):
- symbol = lim[0]
- tex += r"\int"
- if len(lim) > 1:
- if self._settings['mode'] != 'inline' \
- and not self._settings['itex']:
- tex += r"\limits"
- if len(lim) == 3:
- tex += "_{%s}^{%s}" % (self._print(lim[1]),
- self._print(lim[2]))
- if len(lim) == 2:
- tex += "^{%s}" % (self._print(lim[1]))
- symbols.insert(0, r"\, %s%s" % (diff_symbol, self._print(symbol)))
- return r"%s %s%s" % (tex, self.parenthesize(expr.function,
- PRECEDENCE["Mul"],
- is_neg=any(i.could_extract_minus_sign() for i in expr.args),
- strict=True),
- "".join(symbols))
- def _print_Limit(self, expr):
- e, z, z0, dir = expr.args
- tex = r"\lim_{%s \to " % self._print(z)
- if str(dir) == '+-' or z0 in (S.Infinity, S.NegativeInfinity):
- tex += r"%s}" % self._print(z0)
- else:
- tex += r"%s^%s}" % (self._print(z0), self._print(dir))
- if isinstance(e, AssocOp):
- return r"%s\left(%s\right)" % (tex, self._print(e))
- else:
- return r"%s %s" % (tex, self._print(e))
- def _hprint_Function(self, func: str) -> str:
- r'''
- Logic to decide how to render a function to latex
- - if it is a recognized latex name, use the appropriate latex command
- - if it is a single letter, excluding sub- and superscripts, just use that letter
- - if it is a longer name, then put \operatorname{} around it and be
- mindful of undercores in the name
- '''
- func = self._deal_with_super_sub(func)
- superscriptidx = func.find("^")
- subscriptidx = func.find("_")
- if func in accepted_latex_functions:
- name = r"\%s" % func
- elif len(func) == 1 or func.startswith('\\') or subscriptidx == 1 or superscriptidx == 1:
- name = func
- else:
- if superscriptidx > 0 and subscriptidx > 0:
- name = r"\operatorname{%s}%s" %(
- func[:min(subscriptidx,superscriptidx)],
- func[min(subscriptidx,superscriptidx):])
- elif superscriptidx > 0:
- name = r"\operatorname{%s}%s" %(
- func[:superscriptidx],
- func[superscriptidx:])
- elif subscriptidx > 0:
- name = r"\operatorname{%s}%s" %(
- func[:subscriptidx],
- func[subscriptidx:])
- else:
- name = r"\operatorname{%s}" % func
- return name
- def _print_Function(self, expr: Function, exp=None) -> str:
- r'''
- Render functions to LaTeX, handling functions that LaTeX knows about
- e.g., sin, cos, ... by using the proper LaTeX command (\sin, \cos, ...).
- For single-letter function names, render them as regular LaTeX math
- symbols. For multi-letter function names that LaTeX does not know
- about, (e.g., Li, sech) use \operatorname{} so that the function name
- is rendered in Roman font and LaTeX handles spacing properly.
- expr is the expression involving the function
- exp is an exponent
- '''
- func = expr.func.__name__
- if hasattr(self, '_print_' + func) and \
- not isinstance(expr, AppliedUndef):
- return getattr(self, '_print_' + func)(expr, exp)
- else:
- args = [str(self._print(arg)) for arg in expr.args]
- # How inverse trig functions should be displayed, formats are:
- # abbreviated: asin, full: arcsin, power: sin^-1
- inv_trig_style = self._settings['inv_trig_style']
- # If we are dealing with a power-style inverse trig function
- inv_trig_power_case = False
- # If it is applicable to fold the argument brackets
- can_fold_brackets = self._settings['fold_func_brackets'] and \
- len(args) == 1 and \
- not self._needs_function_brackets(expr.args[0])
- inv_trig_table = [
- "asin", "acos", "atan",
- "acsc", "asec", "acot",
- "asinh", "acosh", "atanh",
- "acsch", "asech", "acoth",
- ]
- # If the function is an inverse trig function, handle the style
- if func in inv_trig_table:
- if inv_trig_style == "abbreviated":
- pass
- elif inv_trig_style == "full":
- func = ("ar" if func[-1] == "h" else "arc") + func[1:]
- elif inv_trig_style == "power":
- func = func[1:]
- inv_trig_power_case = True
- # Can never fold brackets if we're raised to a power
- if exp is not None:
- can_fold_brackets = False
- if inv_trig_power_case:
- if func in accepted_latex_functions:
- name = r"\%s^{-1}" % func
- else:
- name = r"\operatorname{%s}^{-1}" % func
- elif exp is not None:
- func_tex = self._hprint_Function(func)
- func_tex = self.parenthesize_super(func_tex)
- name = r'%s^{%s}' % (func_tex, exp)
- else:
- name = self._hprint_Function(func)
- if can_fold_brackets:
- if func in accepted_latex_functions:
- # Wrap argument safely to avoid parse-time conflicts
- # with the function name itself
- name += r" {%s}"
- else:
- name += r"%s"
- else:
- name += r"{\left(%s \right)}"
- if inv_trig_power_case and exp is not None:
- name += r"^{%s}" % exp
- return name % ",".join(args)
- def _print_UndefinedFunction(self, expr):
- return self._hprint_Function(str(expr))
- def _print_ElementwiseApplyFunction(self, expr):
- return r"{%s}_{\circ}\left({%s}\right)" % (
- self._print(expr.function),
- self._print(expr.expr),
- )
- @property
- def _special_function_classes(self):
- from sympy.functions.special.tensor_functions import KroneckerDelta
- from sympy.functions.special.gamma_functions import gamma, lowergamma
- from sympy.functions.special.beta_functions import beta
- from sympy.functions.special.delta_functions import DiracDelta
- from sympy.functions.special.error_functions import Chi
- return {KroneckerDelta: r'\delta',
- gamma: r'\Gamma',
- lowergamma: r'\gamma',
- beta: r'\operatorname{B}',
- DiracDelta: r'\delta',
- Chi: r'\operatorname{Chi}'}
- def _print_FunctionClass(self, expr):
- for cls in self._special_function_classes:
- if issubclass(expr, cls) and expr.__name__ == cls.__name__:
- return self._special_function_classes[cls]
- return self._hprint_Function(str(expr))
- def _print_Lambda(self, expr):
- symbols, expr = expr.args
- if len(symbols) == 1:
- symbols = self._print(symbols[0])
- else:
- symbols = self._print(tuple(symbols))
- tex = r"\left( %s \mapsto %s \right)" % (symbols, self._print(expr))
- return tex
- def _print_IdentityFunction(self, expr):
- return r"\left( x \mapsto x \right)"
- def _hprint_variadic_function(self, expr, exp=None) -> str:
- args = sorted(expr.args, key=default_sort_key)
- texargs = [r"%s" % self._print(symbol) for symbol in args]
- tex = r"\%s\left(%s\right)" % (str(expr.func).lower(),
- ", ".join(texargs))
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- _print_Min = _print_Max = _hprint_variadic_function
- def _print_floor(self, expr, exp=None):
- tex = r"\left\lfloor{%s}\right\rfloor" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_ceiling(self, expr, exp=None):
- tex = r"\left\lceil{%s}\right\rceil" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_log(self, expr, exp=None):
- if not self._settings["ln_notation"]:
- tex = r"\log{\left(%s \right)}" % self._print(expr.args[0])
- else:
- tex = r"\ln{\left(%s \right)}" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_Abs(self, expr, exp=None):
- tex = r"\left|{%s}\right|" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_re(self, expr, exp=None):
- if self._settings['gothic_re_im']:
- tex = r"\Re{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom'])
- else:
- tex = r"\operatorname{{re}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom']))
- return self._do_exponent(tex, exp)
- def _print_im(self, expr, exp=None):
- if self._settings['gothic_re_im']:
- tex = r"\Im{%s}" % self.parenthesize(expr.args[0], PRECEDENCE['Atom'])
- else:
- tex = r"\operatorname{{im}}{{{}}}".format(self.parenthesize(expr.args[0], PRECEDENCE['Atom']))
- return self._do_exponent(tex, exp)
- def _print_Not(self, e):
- from sympy.logic.boolalg import (Equivalent, Implies)
- if isinstance(e.args[0], Equivalent):
- return self._print_Equivalent(e.args[0], r"\not\Leftrightarrow")
- if isinstance(e.args[0], Implies):
- return self._print_Implies(e.args[0], r"\not\Rightarrow")
- if (e.args[0].is_Boolean):
- return r"\neg \left(%s\right)" % self._print(e.args[0])
- else:
- return r"\neg %s" % self._print(e.args[0])
- def _print_LogOp(self, args, char):
- arg = args[0]
- if arg.is_Boolean and not arg.is_Not:
- tex = r"\left(%s\right)" % self._print(arg)
- else:
- tex = r"%s" % self._print(arg)
- for arg in args[1:]:
- if arg.is_Boolean and not arg.is_Not:
- tex += r" %s \left(%s\right)" % (char, self._print(arg))
- else:
- tex += r" %s %s" % (char, self._print(arg))
- return tex
- def _print_And(self, e):
- args = sorted(e.args, key=default_sort_key)
- return self._print_LogOp(args, r"\wedge")
- def _print_Or(self, e):
- args = sorted(e.args, key=default_sort_key)
- return self._print_LogOp(args, r"\vee")
- def _print_Xor(self, e):
- args = sorted(e.args, key=default_sort_key)
- return self._print_LogOp(args, r"\veebar")
- def _print_Implies(self, e, altchar=None):
- return self._print_LogOp(e.args, altchar or r"\Rightarrow")
- def _print_Equivalent(self, e, altchar=None):
- args = sorted(e.args, key=default_sort_key)
- return self._print_LogOp(args, altchar or r"\Leftrightarrow")
- def _print_conjugate(self, expr, exp=None):
- tex = r"\overline{%s}" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_polar_lift(self, expr, exp=None):
- func = r"\operatorname{polar\_lift}"
- arg = r"{\left(%s \right)}" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}%s" % (func, exp, arg)
- else:
- return r"%s%s" % (func, arg)
- def _print_ExpBase(self, expr, exp=None):
- # TODO should exp_polar be printed differently?
- # what about exp_polar(0), exp_polar(1)?
- tex = r"e^{%s}" % self._print(expr.args[0])
- return self._do_exponent(tex, exp)
- def _print_Exp1(self, expr, exp=None):
- return "e"
- def _print_elliptic_k(self, expr, exp=None):
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"K^{%s}%s" % (exp, tex)
- else:
- return r"K%s" % tex
- def _print_elliptic_f(self, expr, exp=None):
- tex = r"\left(%s\middle| %s\right)" % \
- (self._print(expr.args[0]), self._print(expr.args[1]))
- if exp is not None:
- return r"F^{%s}%s" % (exp, tex)
- else:
- return r"F%s" % tex
- def _print_elliptic_e(self, expr, exp=None):
- if len(expr.args) == 2:
- tex = r"\left(%s\middle| %s\right)" % \
- (self._print(expr.args[0]), self._print(expr.args[1]))
- else:
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"E^{%s}%s" % (exp, tex)
- else:
- return r"E%s" % tex
- def _print_elliptic_pi(self, expr, exp=None):
- if len(expr.args) == 3:
- tex = r"\left(%s; %s\middle| %s\right)" % \
- (self._print(expr.args[0]), self._print(expr.args[1]),
- self._print(expr.args[2]))
- else:
- tex = r"\left(%s\middle| %s\right)" % \
- (self._print(expr.args[0]), self._print(expr.args[1]))
- if exp is not None:
- return r"\Pi^{%s}%s" % (exp, tex)
- else:
- return r"\Pi%s" % tex
- def _print_beta(self, expr, exp=None):
- x = expr.args[0]
- # Deal with unevaluated single argument beta
- y = expr.args[0] if len(expr.args) == 1 else expr.args[1]
- tex = rf"\left({x}, {y}\right)"
- if exp is not None:
- return r"\operatorname{B}^{%s}%s" % (exp, tex)
- else:
- return r"\operatorname{B}%s" % tex
- def _print_betainc(self, expr, exp=None, operator='B'):
- largs = [self._print(arg) for arg in expr.args]
- tex = r"\left(%s, %s\right)" % (largs[0], largs[1])
- if exp is not None:
- return r"\operatorname{%s}_{(%s, %s)}^{%s}%s" % (operator, largs[2], largs[3], exp, tex)
- else:
- return r"\operatorname{%s}_{(%s, %s)}%s" % (operator, largs[2], largs[3], tex)
- def _print_betainc_regularized(self, expr, exp=None):
- return self._print_betainc(expr, exp, operator='I')
- def _print_uppergamma(self, expr, exp=None):
- tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]),
- self._print(expr.args[1]))
- if exp is not None:
- return r"\Gamma^{%s}%s" % (exp, tex)
- else:
- return r"\Gamma%s" % tex
- def _print_lowergamma(self, expr, exp=None):
- tex = r"\left(%s, %s\right)" % (self._print(expr.args[0]),
- self._print(expr.args[1]))
- if exp is not None:
- return r"\gamma^{%s}%s" % (exp, tex)
- else:
- return r"\gamma%s" % tex
- def _hprint_one_arg_func(self, expr, exp=None) -> str:
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}%s" % (self._print(expr.func), exp, tex)
- else:
- return r"%s%s" % (self._print(expr.func), tex)
- _print_gamma = _hprint_one_arg_func
- def _print_Chi(self, expr, exp=None):
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"\operatorname{Chi}^{%s}%s" % (exp, tex)
- else:
- return r"\operatorname{Chi}%s" % tex
- def _print_expint(self, expr, exp=None):
- tex = r"\left(%s\right)" % self._print(expr.args[1])
- nu = self._print(expr.args[0])
- if exp is not None:
- return r"\operatorname{E}_{%s}^{%s}%s" % (nu, exp, tex)
- else:
- return r"\operatorname{E}_{%s}%s" % (nu, tex)
- def _print_fresnels(self, expr, exp=None):
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"S^{%s}%s" % (exp, tex)
- else:
- return r"S%s" % tex
- def _print_fresnelc(self, expr, exp=None):
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"C^{%s}%s" % (exp, tex)
- else:
- return r"C%s" % tex
- def _print_subfactorial(self, expr, exp=None):
- tex = r"!%s" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
- if exp is not None:
- return r"\left(%s\right)^{%s}" % (tex, exp)
- else:
- return tex
- def _print_factorial(self, expr, exp=None):
- tex = r"%s!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_factorial2(self, expr, exp=None):
- tex = r"%s!!" % self.parenthesize(expr.args[0], PRECEDENCE["Func"])
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_binomial(self, expr, exp=None):
- tex = r"{\binom{%s}{%s}}" % (self._print(expr.args[0]),
- self._print(expr.args[1]))
- if exp is not None:
- return r"%s^{%s}" % (tex, exp)
- else:
- return tex
- def _print_RisingFactorial(self, expr, exp=None):
- n, k = expr.args
- base = r"%s" % self.parenthesize(n, PRECEDENCE['Func'])
- tex = r"{%s}^{\left(%s\right)}" % (base, self._print(k))
- return self._do_exponent(tex, exp)
- def _print_FallingFactorial(self, expr, exp=None):
- n, k = expr.args
- sub = r"%s" % self.parenthesize(k, PRECEDENCE['Func'])
- tex = r"{\left(%s\right)}_{%s}" % (self._print(n), sub)
- return self._do_exponent(tex, exp)
- def _hprint_BesselBase(self, expr, exp, sym: str) -> str:
- tex = r"%s" % (sym)
- need_exp = False
- if exp is not None:
- if tex.find('^') == -1:
- tex = r"%s^{%s}" % (tex, exp)
- else:
- need_exp = True
- tex = r"%s_{%s}\left(%s\right)" % (tex, self._print(expr.order),
- self._print(expr.argument))
- if need_exp:
- tex = self._do_exponent(tex, exp)
- return tex
- def _hprint_vec(self, vec) -> str:
- if not vec:
- return ""
- s = ""
- for i in vec[:-1]:
- s += "%s, " % self._print(i)
- s += self._print(vec[-1])
- return s
- def _print_besselj(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'J')
- def _print_besseli(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'I')
- def _print_besselk(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'K')
- def _print_bessely(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'Y')
- def _print_yn(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'y')
- def _print_jn(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'j')
- def _print_hankel1(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'H^{(1)}')
- def _print_hankel2(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'H^{(2)}')
- def _print_hn1(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'h^{(1)}')
- def _print_hn2(self, expr, exp=None):
- return self._hprint_BesselBase(expr, exp, 'h^{(2)}')
- def _hprint_airy(self, expr, exp=None, notation="") -> str:
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"%s^{%s}%s" % (notation, exp, tex)
- else:
- return r"%s%s" % (notation, tex)
- def _hprint_airy_prime(self, expr, exp=None, notation="") -> str:
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"{%s^\prime}^{%s}%s" % (notation, exp, tex)
- else:
- return r"%s^\prime%s" % (notation, tex)
- def _print_airyai(self, expr, exp=None):
- return self._hprint_airy(expr, exp, 'Ai')
- def _print_airybi(self, expr, exp=None):
- return self._hprint_airy(expr, exp, 'Bi')
- def _print_airyaiprime(self, expr, exp=None):
- return self._hprint_airy_prime(expr, exp, 'Ai')
- def _print_airybiprime(self, expr, exp=None):
- return self._hprint_airy_prime(expr, exp, 'Bi')
- def _print_hyper(self, expr, exp=None):
- tex = r"{{}_{%s}F_{%s}\left(\begin{matrix} %s \\ %s \end{matrix}" \
- r"\middle| {%s} \right)}" % \
- (self._print(len(expr.ap)), self._print(len(expr.bq)),
- self._hprint_vec(expr.ap), self._hprint_vec(expr.bq),
- self._print(expr.argument))
- if exp is not None:
- tex = r"{%s}^{%s}" % (tex, exp)
- return tex
- def _print_meijerg(self, expr, exp=None):
- tex = r"{G_{%s, %s}^{%s, %s}\left(\begin{matrix} %s & %s \\" \
- r"%s & %s \end{matrix} \middle| {%s} \right)}" % \
- (self._print(len(expr.ap)), self._print(len(expr.bq)),
- self._print(len(expr.bm)), self._print(len(expr.an)),
- self._hprint_vec(expr.an), self._hprint_vec(expr.aother),
- self._hprint_vec(expr.bm), self._hprint_vec(expr.bother),
- self._print(expr.argument))
- if exp is not None:
- tex = r"{%s}^{%s}" % (tex, exp)
- return tex
- def _print_dirichlet_eta(self, expr, exp=None):
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"\eta^{%s}%s" % (exp, tex)
- return r"\eta%s" % tex
- def _print_zeta(self, expr, exp=None):
- if len(expr.args) == 2:
- tex = r"\left(%s, %s\right)" % tuple(map(self._print, expr.args))
- else:
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"\zeta^{%s}%s" % (exp, tex)
- return r"\zeta%s" % tex
- def _print_stieltjes(self, expr, exp=None):
- if len(expr.args) == 2:
- tex = r"_{%s}\left(%s\right)" % tuple(map(self._print, expr.args))
- else:
- tex = r"_{%s}" % self._print(expr.args[0])
- if exp is not None:
- return r"\gamma%s^{%s}" % (tex, exp)
- return r"\gamma%s" % tex
- def _print_lerchphi(self, expr, exp=None):
- tex = r"\left(%s, %s, %s\right)" % tuple(map(self._print, expr.args))
- if exp is None:
- return r"\Phi%s" % tex
- return r"\Phi^{%s}%s" % (exp, tex)
- def _print_polylog(self, expr, exp=None):
- s, z = map(self._print, expr.args)
- tex = r"\left(%s\right)" % z
- if exp is None:
- return r"\operatorname{Li}_{%s}%s" % (s, tex)
- return r"\operatorname{Li}_{%s}^{%s}%s" % (s, exp, tex)
- def _print_jacobi(self, expr, exp=None):
- n, a, b, x = map(self._print, expr.args)
- tex = r"P_{%s}^{\left(%s,%s\right)}\left(%s\right)" % (n, a, b, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_gegenbauer(self, expr, exp=None):
- n, a, x = map(self._print, expr.args)
- tex = r"C_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_chebyshevt(self, expr, exp=None):
- n, x = map(self._print, expr.args)
- tex = r"T_{%s}\left(%s\right)" % (n, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_chebyshevu(self, expr, exp=None):
- n, x = map(self._print, expr.args)
- tex = r"U_{%s}\left(%s\right)" % (n, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_legendre(self, expr, exp=None):
- n, x = map(self._print, expr.args)
- tex = r"P_{%s}\left(%s\right)" % (n, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_assoc_legendre(self, expr, exp=None):
- n, a, x = map(self._print, expr.args)
- tex = r"P_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_hermite(self, expr, exp=None):
- n, x = map(self._print, expr.args)
- tex = r"H_{%s}\left(%s\right)" % (n, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_laguerre(self, expr, exp=None):
- n, x = map(self._print, expr.args)
- tex = r"L_{%s}\left(%s\right)" % (n, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_assoc_laguerre(self, expr, exp=None):
- n, a, x = map(self._print, expr.args)
- tex = r"L_{%s}^{\left(%s\right)}\left(%s\right)" % (n, a, x)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_Ynm(self, expr, exp=None):
- n, m, theta, phi = map(self._print, expr.args)
- tex = r"Y_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def _print_Znm(self, expr, exp=None):
- n, m, theta, phi = map(self._print, expr.args)
- tex = r"Z_{%s}^{%s}\left(%s,%s\right)" % (n, m, theta, phi)
- if exp is not None:
- tex = r"\left(" + tex + r"\right)^{%s}" % (exp)
- return tex
- def __print_mathieu_functions(self, character, args, prime=False, exp=None):
- a, q, z = map(self._print, args)
- sup = r"^{\prime}" if prime else ""
- exp = "" if not exp else "^{%s}" % exp
- return r"%s%s\left(%s, %s, %s\right)%s" % (character, sup, a, q, z, exp)
- def _print_mathieuc(self, expr, exp=None):
- return self.__print_mathieu_functions("C", expr.args, exp=exp)
- def _print_mathieus(self, expr, exp=None):
- return self.__print_mathieu_functions("S", expr.args, exp=exp)
- def _print_mathieucprime(self, expr, exp=None):
- return self.__print_mathieu_functions("C", expr.args, prime=True, exp=exp)
- def _print_mathieusprime(self, expr, exp=None):
- return self.__print_mathieu_functions("S", expr.args, prime=True, exp=exp)
- def _print_Rational(self, expr):
- if expr.q != 1:
- sign = ""
- p = expr.p
- if expr.p < 0:
- sign = "- "
- p = -p
- if self._settings['fold_short_frac']:
- return r"%s%d / %d" % (sign, p, expr.q)
- return r"%s\frac{%d}{%d}" % (sign, p, expr.q)
- else:
- return self._print(expr.p)
- def _print_Order(self, expr):
- s = self._print(expr.expr)
- if expr.point and any(p != S.Zero for p in expr.point) or \
- len(expr.variables) > 1:
- s += '; '
- if len(expr.variables) > 1:
- s += self._print(expr.variables)
- elif expr.variables:
- s += self._print(expr.variables[0])
- s += r'\rightarrow '
- if len(expr.point) > 1:
- s += self._print(expr.point)
- else:
- s += self._print(expr.point[0])
- return r"O\left(%s\right)" % s
- def _print_Symbol(self, expr: Symbol, style='plain'):
- name: str = self._settings['symbol_names'].get(expr)
- if name is not None:
- return name
- return self._deal_with_super_sub(expr.name, style=style)
- _print_RandomSymbol = _print_Symbol
- def _deal_with_super_sub(self, string: str, style='plain') -> str:
- if '{' in string:
- name, supers, subs = string, [], []
- else:
- name, supers, subs = split_super_sub(string)
- name = translate(name)
- supers = [translate(sup) for sup in supers]
- subs = [translate(sub) for sub in subs]
- # apply the style only to the name
- if style == 'bold':
- name = "\\mathbf{{{}}}".format(name)
- # glue all items together:
- if supers:
- name += "^{%s}" % " ".join(supers)
- if subs:
- name += "_{%s}" % " ".join(subs)
- return name
- def _print_Relational(self, expr):
- if self._settings['itex']:
- gt = r"\gt"
- lt = r"\lt"
- else:
- gt = ">"
- lt = "<"
- charmap = {
- "==": "=",
- ">": gt,
- "<": lt,
- ">=": r"\geq",
- "<=": r"\leq",
- "!=": r"\neq",
- }
- return "%s %s %s" % (self._print(expr.lhs),
- charmap[expr.rel_op], self._print(expr.rhs))
- def _print_Piecewise(self, expr):
- ecpairs = [r"%s & \text{for}\: %s" % (self._print(e), self._print(c))
- for e, c in expr.args[:-1]]
- if expr.args[-1].cond == true:
- ecpairs.append(r"%s & \text{otherwise}" %
- self._print(expr.args[-1].expr))
- else:
- ecpairs.append(r"%s & \text{for}\: %s" %
- (self._print(expr.args[-1].expr),
- self._print(expr.args[-1].cond)))
- tex = r"\begin{cases} %s \end{cases}"
- return tex % r" \\".join(ecpairs)
- def _print_matrix_contents(self, expr):
- lines = []
- for line in range(expr.rows): # horrible, should be 'rows'
- lines.append(" & ".join([self._print(i) for i in expr[line, :]]))
- mat_str = self._settings['mat_str']
- if mat_str is None:
- if self._settings['mode'] == 'inline':
- mat_str = 'smallmatrix'
- else:
- if (expr.cols <= 10) is True:
- mat_str = 'matrix'
- else:
- mat_str = 'array'
- out_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}'
- out_str = out_str.replace('%MATSTR%', mat_str)
- if mat_str == 'array':
- out_str = out_str.replace('%s', '{' + 'c'*expr.cols + '}%s')
- return out_str % r"\\".join(lines)
- def _print_MatrixBase(self, expr):
- out_str = self._print_matrix_contents(expr)
- if self._settings['mat_delim']:
- left_delim = self._settings['mat_delim']
- right_delim = self._delim_dict[left_delim]
- out_str = r'\left' + left_delim + out_str + \
- r'\right' + right_delim
- return out_str
- def _print_MatrixElement(self, expr):
- return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True)\
- + '_{%s, %s}' % (self._print(expr.i), self._print(expr.j))
- def _print_MatrixSlice(self, expr):
- def latexslice(x, dim):
- x = list(x)
- if x[2] == 1:
- del x[2]
- if x[0] == 0:
- x[0] = None
- if x[1] == dim:
- x[1] = None
- return ':'.join(self._print(xi) if xi is not None else '' for xi in x)
- return (self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) + r'\left[' +
- latexslice(expr.rowslice, expr.parent.rows) + ', ' +
- latexslice(expr.colslice, expr.parent.cols) + r'\right]')
- def _print_BlockMatrix(self, expr):
- return self._print(expr.blocks)
- def _print_Transpose(self, expr):
- mat = expr.arg
- from sympy.matrices import MatrixSymbol, BlockMatrix
- if (not isinstance(mat, MatrixSymbol) and
- not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr):
- return r"\left(%s\right)^{T}" % self._print(mat)
- else:
- s = self.parenthesize(mat, precedence_traditional(expr), True)
- if '^' in s:
- return r"\left(%s\right)^{T}" % s
- else:
- return "%s^{T}" % s
- def _print_Trace(self, expr):
- mat = expr.arg
- return r"\operatorname{tr}\left(%s \right)" % self._print(mat)
- def _print_Adjoint(self, expr):
- mat = expr.arg
- from sympy.matrices import MatrixSymbol, BlockMatrix
- if (not isinstance(mat, MatrixSymbol) and
- not isinstance(mat, BlockMatrix) and mat.is_MatrixExpr):
- return r"\left(%s\right)^{\dagger}" % self._print(mat)
- else:
- s = self.parenthesize(mat, precedence_traditional(expr), True)
- if '^' in s:
- return r"\left(%s\right)^{\dagger}" % s
- else:
- return r"%s^{\dagger}" % s
- def _print_MatMul(self, expr):
- from sympy import MatMul
- # Parenthesize nested MatMul but not other types of Mul objects:
- parens = lambda x: self._print(x) if isinstance(x, Mul) and not isinstance(x, MatMul) else \
- self.parenthesize(x, precedence_traditional(expr), False)
- args = list(expr.args)
- if expr.could_extract_minus_sign():
- if args[0] == -1:
- args = args[1:]
- else:
- args[0] = -args[0]
- return '- ' + ' '.join(map(parens, args))
- else:
- return ' '.join(map(parens, args))
- def _print_Determinant(self, expr):
- mat = expr.arg
- if mat.is_MatrixExpr:
- from sympy.matrices.expressions.blockmatrix import BlockMatrix
- if isinstance(mat, BlockMatrix):
- return r"\left|{%s}\right|" % self._print_matrix_contents(mat.blocks)
- return r"\left|{%s}\right|" % self._print(mat)
- return r"\left|{%s}\right|" % self._print_matrix_contents(mat)
- def _print_Mod(self, expr, exp=None):
- if exp is not None:
- return r'\left(%s \bmod %s\right)^{%s}' % \
- (self.parenthesize(expr.args[0], PRECEDENCE['Mul'],
- strict=True),
- self.parenthesize(expr.args[1], PRECEDENCE['Mul'],
- strict=True),
- exp)
- return r'%s \bmod %s' % (self.parenthesize(expr.args[0],
- PRECEDENCE['Mul'],
- strict=True),
- self.parenthesize(expr.args[1],
- PRECEDENCE['Mul'],
- strict=True))
- def _print_HadamardProduct(self, expr):
- args = expr.args
- prec = PRECEDENCE['Pow']
- parens = self.parenthesize
- return r' \circ '.join(
- (parens(arg, prec, strict=True) for arg in args))
- def _print_HadamardPower(self, expr):
- if precedence_traditional(expr.exp) < PRECEDENCE["Mul"]:
- template = r"%s^{\circ \left({%s}\right)}"
- else:
- template = r"%s^{\circ {%s}}"
- return self._helper_print_standard_power(expr, template)
- def _print_KroneckerProduct(self, expr):
- args = expr.args
- prec = PRECEDENCE['Pow']
- parens = self.parenthesize
- return r' \otimes '.join(
- (parens(arg, prec, strict=True) for arg in args))
- def _print_MatPow(self, expr):
- base, exp = expr.base, expr.exp
- from sympy.matrices import MatrixSymbol
- if not isinstance(base, MatrixSymbol) and base.is_MatrixExpr:
- return "\\left(%s\\right)^{%s}" % (self._print(base),
- self._print(exp))
- else:
- base_str = self._print(base)
- if '^' in base_str:
- return r"\left(%s\right)^{%s}" % (base_str, self._print(exp))
- else:
- return "%s^{%s}" % (base_str, self._print(exp))
- def _print_MatrixSymbol(self, expr):
- return self._print_Symbol(expr, style=self._settings[
- 'mat_symbol_style'])
- def _print_ZeroMatrix(self, Z):
- return "0" if self._settings[
- 'mat_symbol_style'] == 'plain' else r"\mathbf{0}"
- def _print_OneMatrix(self, O):
- return "1" if self._settings[
- 'mat_symbol_style'] == 'plain' else r"\mathbf{1}"
- def _print_Identity(self, I):
- return r"\mathbb{I}" if self._settings[
- 'mat_symbol_style'] == 'plain' else r"\mathbf{I}"
- def _print_PermutationMatrix(self, P):
- perm_str = self._print(P.args[0])
- return "P_{%s}" % perm_str
- def _print_NDimArray(self, expr: NDimArray):
- if expr.rank() == 0:
- return self._print(expr[()])
- mat_str = self._settings['mat_str']
- if mat_str is None:
- if self._settings['mode'] == 'inline':
- mat_str = 'smallmatrix'
- else:
- if (expr.rank() == 0) or (expr.shape[-1] <= 10):
- mat_str = 'matrix'
- else:
- mat_str = 'array'
- block_str = r'\begin{%MATSTR%}%s\end{%MATSTR%}'
- block_str = block_str.replace('%MATSTR%', mat_str)
- if mat_str == 'array':
- block_str= block_str.replace('%s','{}%s')
- if self._settings['mat_delim']:
- left_delim: str = self._settings['mat_delim']
- right_delim = self._delim_dict[left_delim]
- block_str = r'\left' + left_delim + block_str + \
- r'\right' + right_delim
- if expr.rank() == 0:
- return block_str % ""
- level_str: list[list[str]] = [[] for i in range(expr.rank() + 1)]
- shape_ranges = [list(range(i)) for i in expr.shape]
- for outer_i in itertools.product(*shape_ranges):
- level_str[-1].append(self._print(expr[outer_i]))
- even = True
- for back_outer_i in range(expr.rank()-1, -1, -1):
- if len(level_str[back_outer_i+1]) < expr.shape[back_outer_i]:
- break
- if even:
- level_str[back_outer_i].append(
- r" & ".join(level_str[back_outer_i+1]))
- else:
- level_str[back_outer_i].append(
- block_str % (r"\\".join(level_str[back_outer_i+1])))
- if len(level_str[back_outer_i+1]) == 1:
- level_str[back_outer_i][-1] = r"\left[" + \
- level_str[back_outer_i][-1] + r"\right]"
- even = not even
- level_str[back_outer_i+1] = []
- out_str = level_str[0][0]
- if expr.rank() % 2 == 1:
- out_str = block_str % out_str
- return out_str
- def _printer_tensor_indices(self, name, indices, index_map: dict):
- out_str = self._print(name)
- last_valence = None
- prev_map = None
- for index in indices:
- new_valence = index.is_up
- if ((index in index_map) or prev_map) and \
- last_valence == new_valence:
- out_str += ","
- if last_valence != new_valence:
- if last_valence is not None:
- out_str += "}"
- if index.is_up:
- out_str += "{}^{"
- else:
- out_str += "{}_{"
- out_str += self._print(index.args[0])
- if index in index_map:
- out_str += "="
- out_str += self._print(index_map[index])
- prev_map = True
- else:
- prev_map = False
- last_valence = new_valence
- if last_valence is not None:
- out_str += "}"
- return out_str
- def _print_Tensor(self, expr):
- name = expr.args[0].args[0]
- indices = expr.get_indices()
- return self._printer_tensor_indices(name, indices, {})
- def _print_TensorElement(self, expr):
- name = expr.expr.args[0].args[0]
- indices = expr.expr.get_indices()
- index_map = expr.index_map
- return self._printer_tensor_indices(name, indices, index_map)
- def _print_TensMul(self, expr):
- # prints expressions like "A(a)", "3*A(a)", "(1+x)*A(a)"
- sign, args = expr._get_args_for_traditional_printer()
- return sign + "".join(
- [self.parenthesize(arg, precedence(expr)) for arg in args]
- )
- def _print_TensAdd(self, expr):
- a = []
- args = expr.args
- for x in args:
- a.append(self.parenthesize(x, precedence(expr)))
- a.sort()
- s = ' + '.join(a)
- s = s.replace('+ -', '- ')
- return s
- def _print_TensorIndex(self, expr):
- return "{}%s{%s}" % (
- "^" if expr.is_up else "_",
- self._print(expr.args[0])
- )
- def _print_PartialDerivative(self, expr):
- if len(expr.variables) == 1:
- return r"\frac{\partial}{\partial {%s}}{%s}" % (
- self._print(expr.variables[0]),
- self.parenthesize(expr.expr, PRECEDENCE["Mul"], False)
- )
- else:
- return r"\frac{\partial^{%s}}{%s}{%s}" % (
- len(expr.variables),
- " ".join([r"\partial {%s}" % self._print(i) for i in expr.variables]),
- self.parenthesize(expr.expr, PRECEDENCE["Mul"], False)
- )
- def _print_ArraySymbol(self, expr):
- return self._print(expr.name)
- def _print_ArrayElement(self, expr):
- return "{{%s}_{%s}}" % (
- self.parenthesize(expr.name, PRECEDENCE["Func"], True),
- ", ".join([f"{self._print(i)}" for i in expr.indices]))
- def _print_UniversalSet(self, expr):
- return r"\mathbb{U}"
- def _print_frac(self, expr, exp=None):
- if exp is None:
- return r"\operatorname{frac}{\left(%s\right)}" % self._print(expr.args[0])
- else:
- return r"\operatorname{frac}{\left(%s\right)}^{%s}" % (
- self._print(expr.args[0]), exp)
- def _print_tuple(self, expr):
- if self._settings['decimal_separator'] == 'comma':
- sep = ";"
- elif self._settings['decimal_separator'] == 'period':
- sep = ","
- else:
- raise ValueError('Unknown Decimal Separator')
- if len(expr) == 1:
- # 1-tuple needs a trailing separator
- return self._add_parens_lspace(self._print(expr[0]) + sep)
- else:
- return self._add_parens_lspace(
- (sep + r" \ ").join([self._print(i) for i in expr]))
- def _print_TensorProduct(self, expr):
- elements = [self._print(a) for a in expr.args]
- return r' \otimes '.join(elements)
- def _print_WedgeProduct(self, expr):
- elements = [self._print(a) for a in expr.args]
- return r' \wedge '.join(elements)
- def _print_Tuple(self, expr):
- return self._print_tuple(expr)
- def _print_list(self, expr):
- if self._settings['decimal_separator'] == 'comma':
- return r"\left[ %s\right]" % \
- r"; \ ".join([self._print(i) for i in expr])
- elif self._settings['decimal_separator'] == 'period':
- return r"\left[ %s\right]" % \
- r", \ ".join([self._print(i) for i in expr])
- else:
- raise ValueError('Unknown Decimal Separator')
- def _print_dict(self, d):
- keys = sorted(d.keys(), key=default_sort_key)
- items = []
- for key in keys:
- val = d[key]
- items.append("%s : %s" % (self._print(key), self._print(val)))
- return r"\left\{ %s\right\}" % r", \ ".join(items)
- def _print_Dict(self, expr):
- return self._print_dict(expr)
- def _print_DiracDelta(self, expr, exp=None):
- if len(expr.args) == 1 or expr.args[1] == 0:
- tex = r"\delta\left(%s\right)" % self._print(expr.args[0])
- else:
- tex = r"\delta^{\left( %s \right)}\left( %s \right)" % (
- self._print(expr.args[1]), self._print(expr.args[0]))
- if exp:
- tex = r"\left(%s\right)^{%s}" % (tex, exp)
- return tex
- def _print_SingularityFunction(self, expr, exp=None):
- shift = self._print(expr.args[0] - expr.args[1])
- power = self._print(expr.args[2])
- tex = r"{\left\langle %s \right\rangle}^{%s}" % (shift, power)
- if exp is not None:
- tex = r"{\left({\langle %s \rangle}^{%s}\right)}^{%s}" % (shift, power, exp)
- return tex
- def _print_Heaviside(self, expr, exp=None):
- pargs = ', '.join(self._print(arg) for arg in expr.pargs)
- tex = r"\theta\left(%s\right)" % pargs
- if exp:
- tex = r"\left(%s\right)^{%s}" % (tex, exp)
- return tex
- def _print_KroneckerDelta(self, expr, exp=None):
- i = self._print(expr.args[0])
- j = self._print(expr.args[1])
- if expr.args[0].is_Atom and expr.args[1].is_Atom:
- tex = r'\delta_{%s %s}' % (i, j)
- else:
- tex = r'\delta_{%s, %s}' % (i, j)
- if exp is not None:
- tex = r'\left(%s\right)^{%s}' % (tex, exp)
- return tex
- def _print_LeviCivita(self, expr, exp=None):
- indices = map(self._print, expr.args)
- if all(x.is_Atom for x in expr.args):
- tex = r'\varepsilon_{%s}' % " ".join(indices)
- else:
- tex = r'\varepsilon_{%s}' % ", ".join(indices)
- if exp:
- tex = r'\left(%s\right)^{%s}' % (tex, exp)
- return tex
- def _print_RandomDomain(self, d):
- if hasattr(d, 'as_boolean'):
- return '\\text{Domain: }' + self._print(d.as_boolean())
- elif hasattr(d, 'set'):
- return ('\\text{Domain: }' + self._print(d.symbols) + ' \\in ' +
- self._print(d.set))
- elif hasattr(d, 'symbols'):
- return '\\text{Domain on }' + self._print(d.symbols)
- else:
- return self._print(None)
- def _print_FiniteSet(self, s):
- items = sorted(s.args, key=default_sort_key)
- return self._print_set(items)
- def _print_set(self, s):
- items = sorted(s, key=default_sort_key)
- if self._settings['decimal_separator'] == 'comma':
- items = "; ".join(map(self._print, items))
- elif self._settings['decimal_separator'] == 'period':
- items = ", ".join(map(self._print, items))
- else:
- raise ValueError('Unknown Decimal Separator')
- return r"\left\{%s\right\}" % items
- _print_frozenset = _print_set
- def _print_Range(self, s):
- def _print_symbolic_range():
- # Symbolic Range that cannot be resolved
- if s.args[0] == 0:
- if s.args[2] == 1:
- cont = self._print(s.args[1])
- else:
- cont = ", ".join(self._print(arg) for arg in s.args)
- else:
- if s.args[2] == 1:
- cont = ", ".join(self._print(arg) for arg in s.args[:2])
- else:
- cont = ", ".join(self._print(arg) for arg in s.args)
- return(f"\\text{{Range}}\\left({cont}\\right)")
- dots = object()
- if s.start.is_infinite and s.stop.is_infinite:
- if s.step.is_positive:
- printset = dots, -1, 0, 1, dots
- else:
- printset = dots, 1, 0, -1, dots
- elif s.start.is_infinite:
- printset = dots, s[-1] - s.step, s[-1]
- elif s.stop.is_infinite:
- it = iter(s)
- printset = next(it), next(it), dots
- elif s.is_empty is not None:
- if (s.size < 4) == True:
- printset = tuple(s)
- elif s.is_iterable:
- it = iter(s)
- printset = next(it), next(it), dots, s[-1]
- else:
- return _print_symbolic_range()
- else:
- return _print_symbolic_range()
- return (r"\left\{" +
- r", ".join(self._print(el) if el is not dots else r'\ldots' for el in printset) +
- r"\right\}")
- def __print_number_polynomial(self, expr, letter, exp=None):
- if len(expr.args) == 2:
- if exp is not None:
- return r"%s_{%s}^{%s}\left(%s\right)" % (letter,
- self._print(expr.args[0]), exp,
- self._print(expr.args[1]))
- return r"%s_{%s}\left(%s\right)" % (letter,
- self._print(expr.args[0]), self._print(expr.args[1]))
- tex = r"%s_{%s}" % (letter, self._print(expr.args[0]))
- if exp is not None:
- tex = r"%s^{%s}" % (tex, exp)
- return tex
- def _print_bernoulli(self, expr, exp=None):
- return self.__print_number_polynomial(expr, "B", exp)
- def _print_genocchi(self, expr, exp=None):
- return self.__print_number_polynomial(expr, "G", exp)
- def _print_bell(self, expr, exp=None):
- if len(expr.args) == 3:
- tex1 = r"B_{%s, %s}" % (self._print(expr.args[0]),
- self._print(expr.args[1]))
- tex2 = r"\left(%s\right)" % r", ".join(self._print(el) for
- el in expr.args[2])
- if exp is not None:
- tex = r"%s^{%s}%s" % (tex1, exp, tex2)
- else:
- tex = tex1 + tex2
- return tex
- return self.__print_number_polynomial(expr, "B", exp)
- def _print_fibonacci(self, expr, exp=None):
- return self.__print_number_polynomial(expr, "F", exp)
- def _print_lucas(self, expr, exp=None):
- tex = r"L_{%s}" % self._print(expr.args[0])
- if exp is not None:
- tex = r"%s^{%s}" % (tex, exp)
- return tex
- def _print_tribonacci(self, expr, exp=None):
- return self.__print_number_polynomial(expr, "T", exp)
- def _print_SeqFormula(self, s):
- dots = object()
- if len(s.start.free_symbols) > 0 or len(s.stop.free_symbols) > 0:
- return r"\left\{%s\right\}_{%s=%s}^{%s}" % (
- self._print(s.formula),
- self._print(s.variables[0]),
- self._print(s.start),
- self._print(s.stop)
- )
- if s.start is S.NegativeInfinity:
- stop = s.stop
- printset = (dots, s.coeff(stop - 3), s.coeff(stop - 2),
- s.coeff(stop - 1), s.coeff(stop))
- elif s.stop is S.Infinity or s.length > 4:
- printset = s[:4]
- printset.append(dots)
- else:
- printset = tuple(s)
- return (r"\left[" +
- r", ".join(self._print(el) if el is not dots else r'\ldots' for el in printset) +
- r"\right]")
- _print_SeqPer = _print_SeqFormula
- _print_SeqAdd = _print_SeqFormula
- _print_SeqMul = _print_SeqFormula
- def _print_Interval(self, i):
- if i.start == i.end:
- return r"\left\{%s\right\}" % self._print(i.start)
- else:
- if i.left_open:
- left = '('
- else:
- left = '['
- if i.right_open:
- right = ')'
- else:
- right = ']'
- return r"\left%s%s, %s\right%s" % \
- (left, self._print(i.start), self._print(i.end), right)
- def _print_AccumulationBounds(self, i):
- return r"\left\langle %s, %s\right\rangle" % \
- (self._print(i.min), self._print(i.max))
- def _print_Union(self, u):
- prec = precedence_traditional(u)
- args_str = [self.parenthesize(i, prec) for i in u.args]
- return r" \cup ".join(args_str)
- def _print_Complement(self, u):
- prec = precedence_traditional(u)
- args_str = [self.parenthesize(i, prec) for i in u.args]
- return r" \setminus ".join(args_str)
- def _print_Intersection(self, u):
- prec = precedence_traditional(u)
- args_str = [self.parenthesize(i, prec) for i in u.args]
- return r" \cap ".join(args_str)
- def _print_SymmetricDifference(self, u):
- prec = precedence_traditional(u)
- args_str = [self.parenthesize(i, prec) for i in u.args]
- return r" \triangle ".join(args_str)
- def _print_ProductSet(self, p):
- prec = precedence_traditional(p)
- if len(p.sets) >= 1 and not has_variety(p.sets):
- return self.parenthesize(p.sets[0], prec) + "^{%d}" % len(p.sets)
- return r" \times ".join(
- self.parenthesize(set, prec) for set in p.sets)
- def _print_EmptySet(self, e):
- return r"\emptyset"
- def _print_Naturals(self, n):
- return r"\mathbb{N}"
- def _print_Naturals0(self, n):
- return r"\mathbb{N}_0"
- def _print_Integers(self, i):
- return r"\mathbb{Z}"
- def _print_Rationals(self, i):
- return r"\mathbb{Q}"
- def _print_Reals(self, i):
- return r"\mathbb{R}"
- def _print_Complexes(self, i):
- return r"\mathbb{C}"
- def _print_ImageSet(self, s):
- expr = s.lamda.expr
- sig = s.lamda.signature
- xys = ((self._print(x), self._print(y)) for x, y in zip(sig, s.base_sets))
- xinys = r", ".join(r"%s \in %s" % xy for xy in xys)
- return r"\left\{%s\; \middle|\; %s\right\}" % (self._print(expr), xinys)
- def _print_ConditionSet(self, s):
- vars_print = ', '.join([self._print(var) for var in Tuple(s.sym)])
- if s.base_set is S.UniversalSet:
- return r"\left\{%s\; \middle|\; %s \right\}" % \
- (vars_print, self._print(s.condition))
- return r"\left\{%s\; \middle|\; %s \in %s \wedge %s \right\}" % (
- vars_print,
- vars_print,
- self._print(s.base_set),
- self._print(s.condition))
- def _print_PowerSet(self, expr):
- arg_print = self._print(expr.args[0])
- return r"\mathcal{{P}}\left({}\right)".format(arg_print)
- def _print_ComplexRegion(self, s):
- vars_print = ', '.join([self._print(var) for var in s.variables])
- return r"\left\{%s\; \middle|\; %s \in %s \right\}" % (
- self._print(s.expr),
- vars_print,
- self._print(s.sets))
- def _print_Contains(self, e):
- return r"%s \in %s" % tuple(self._print(a) for a in e.args)
- def _print_FourierSeries(self, s):
- if s.an.formula is S.Zero and s.bn.formula is S.Zero:
- return self._print(s.a0)
- return self._print_Add(s.truncate()) + r' + \ldots'
- def _print_FormalPowerSeries(self, s):
- return self._print_Add(s.infinite)
- def _print_FiniteField(self, expr):
- return r"\mathbb{F}_{%s}" % expr.mod
- def _print_IntegerRing(self, expr):
- return r"\mathbb{Z}"
- def _print_RationalField(self, expr):
- return r"\mathbb{Q}"
- def _print_RealField(self, expr):
- return r"\mathbb{R}"
- def _print_ComplexField(self, expr):
- return r"\mathbb{C}"
- def _print_PolynomialRing(self, expr):
- domain = self._print(expr.domain)
- symbols = ", ".join(map(self._print, expr.symbols))
- return r"%s\left[%s\right]" % (domain, symbols)
- def _print_FractionField(self, expr):
- domain = self._print(expr.domain)
- symbols = ", ".join(map(self._print, expr.symbols))
- return r"%s\left(%s\right)" % (domain, symbols)
- def _print_PolynomialRingBase(self, expr):
- domain = self._print(expr.domain)
- symbols = ", ".join(map(self._print, expr.symbols))
- inv = ""
- if not expr.is_Poly:
- inv = r"S_<^{-1}"
- return r"%s%s\left[%s\right]" % (inv, domain, symbols)
- def _print_Poly(self, poly):
- cls = poly.__class__.__name__
- terms = []
- for monom, coeff in poly.terms():
- s_monom = ''
- for i, exp in enumerate(monom):
- if exp > 0:
- if exp == 1:
- s_monom += self._print(poly.gens[i])
- else:
- s_monom += self._print(pow(poly.gens[i], exp))
- if coeff.is_Add:
- if s_monom:
- s_coeff = r"\left(%s\right)" % self._print(coeff)
- else:
- s_coeff = self._print(coeff)
- else:
- if s_monom:
- if coeff is S.One:
- terms.extend(['+', s_monom])
- continue
- if coeff is S.NegativeOne:
- terms.extend(['-', s_monom])
- continue
- s_coeff = self._print(coeff)
- if not s_monom:
- s_term = s_coeff
- else:
- s_term = s_coeff + " " + s_monom
- if s_term.startswith('-'):
- terms.extend(['-', s_term[1:]])
- else:
- terms.extend(['+', s_term])
- if terms[0] in ('-', '+'):
- modifier = terms.pop(0)
- if modifier == '-':
- terms[0] = '-' + terms[0]
- expr = ' '.join(terms)
- gens = list(map(self._print, poly.gens))
- domain = "domain=%s" % self._print(poly.get_domain())
- args = ", ".join([expr] + gens + [domain])
- if cls in accepted_latex_functions:
- tex = r"\%s {\left(%s \right)}" % (cls, args)
- else:
- tex = r"\operatorname{%s}{\left( %s \right)}" % (cls, args)
- return tex
- def _print_ComplexRootOf(self, root):
- cls = root.__class__.__name__
- if cls == "ComplexRootOf":
- cls = "CRootOf"
- expr = self._print(root.expr)
- index = root.index
- if cls in accepted_latex_functions:
- return r"\%s {\left(%s, %d\right)}" % (cls, expr, index)
- else:
- return r"\operatorname{%s} {\left(%s, %d\right)}" % (cls, expr,
- index)
- def _print_RootSum(self, expr):
- cls = expr.__class__.__name__
- args = [self._print(expr.expr)]
- if expr.fun is not S.IdentityFunction:
- args.append(self._print(expr.fun))
- if cls in accepted_latex_functions:
- return r"\%s {\left(%s\right)}" % (cls, ", ".join(args))
- else:
- return r"\operatorname{%s} {\left(%s\right)}" % (cls,
- ", ".join(args))
- def _print_OrdinalOmega(self, expr):
- return r"\omega"
- def _print_OmegaPower(self, expr):
- exp, mul = expr.args
- if mul != 1:
- if exp != 1:
- return r"{} \omega^{{{}}}".format(mul, exp)
- else:
- return r"{} \omega".format(mul)
- else:
- if exp != 1:
- return r"\omega^{{{}}}".format(exp)
- else:
- return r"\omega"
- def _print_Ordinal(self, expr):
- return " + ".join([self._print(arg) for arg in expr.args])
- def _print_PolyElement(self, poly):
- mul_symbol = self._settings['mul_symbol_latex']
- return poly.str(self, PRECEDENCE, "{%s}^{%d}", mul_symbol)
- def _print_FracElement(self, frac):
- if frac.denom == 1:
- return self._print(frac.numer)
- else:
- numer = self._print(frac.numer)
- denom = self._print(frac.denom)
- return r"\frac{%s}{%s}" % (numer, denom)
- def _print_euler(self, expr, exp=None):
- m, x = (expr.args[0], None) if len(expr.args) == 1 else expr.args
- tex = r"E_{%s}" % self._print(m)
- if exp is not None:
- tex = r"%s^{%s}" % (tex, exp)
- if x is not None:
- tex = r"%s\left(%s\right)" % (tex, self._print(x))
- return tex
- def _print_catalan(self, expr, exp=None):
- tex = r"C_{%s}" % self._print(expr.args[0])
- if exp is not None:
- tex = r"%s^{%s}" % (tex, exp)
- return tex
- def _print_UnifiedTransform(self, expr, s, inverse=False):
- 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]))
- def _print_MellinTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'M')
- def _print_InverseMellinTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'M', True)
- def _print_LaplaceTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'L')
- def _print_InverseLaplaceTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'L', True)
- def _print_FourierTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'F')
- def _print_InverseFourierTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'F', True)
- def _print_SineTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'SIN')
- def _print_InverseSineTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'SIN', True)
- def _print_CosineTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'COS')
- def _print_InverseCosineTransform(self, expr):
- return self._print_UnifiedTransform(expr, 'COS', True)
- def _print_DMP(self, p):
- try:
- if p.ring is not None:
- # TODO incorporate order
- return self._print(p.ring.to_sympy(p))
- except SympifyError:
- pass
- return self._print(repr(p))
- def _print_DMF(self, p):
- return self._print_DMP(p)
- def _print_Object(self, object):
- return self._print(Symbol(object.name))
- def _print_LambertW(self, expr, exp=None):
- arg0 = self._print(expr.args[0])
- exp = r"^{%s}" % (exp,) if exp is not None else ""
- if len(expr.args) == 1:
- result = r"W%s\left(%s\right)" % (exp, arg0)
- else:
- arg1 = self._print(expr.args[1])
- result = "W{0}_{{{1}}}\\left({2}\\right)".format(exp, arg1, arg0)
- return result
- def _print_Expectation(self, expr):
- return r"\operatorname{{E}}\left[{}\right]".format(self._print(expr.args[0]))
- def _print_Variance(self, expr):
- return r"\operatorname{{Var}}\left({}\right)".format(self._print(expr.args[0]))
- def _print_Covariance(self, expr):
- return r"\operatorname{{Cov}}\left({}\right)".format(", ".join(self._print(arg) for arg in expr.args))
- def _print_Probability(self, expr):
- return r"\operatorname{{P}}\left({}\right)".format(self._print(expr.args[0]))
- def _print_Morphism(self, morphism):
- domain = self._print(morphism.domain)
- codomain = self._print(morphism.codomain)
- return "%s\\rightarrow %s" % (domain, codomain)
- def _print_TransferFunction(self, expr):
- num, den = self._print(expr.num), self._print(expr.den)
- return r"\frac{%s}{%s}" % (num, den)
- def _print_Series(self, expr):
- args = list(expr.args)
- parens = lambda x: self.parenthesize(x, precedence_traditional(expr),
- False)
- return ' '.join(map(parens, args))
- def _print_MIMOSeries(self, expr):
- from sympy.physics.control.lti import MIMOParallel
- args = list(expr.args)[::-1]
- parens = lambda x: self.parenthesize(x, precedence_traditional(expr),
- False) if isinstance(x, MIMOParallel) else self._print(x)
- return r"\cdot".join(map(parens, args))
- def _print_Parallel(self, expr):
- return ' + '.join(map(self._print, expr.args))
- def _print_MIMOParallel(self, expr):
- return ' + '.join(map(self._print, expr.args))
- def _print_Feedback(self, expr):
- from sympy.physics.control import TransferFunction, Series
- num, tf = expr.sys1, TransferFunction(1, 1, expr.var)
- num_arg_list = list(num.args) if isinstance(num, Series) else [num]
- den_arg_list = list(expr.sys2.args) if \
- isinstance(expr.sys2, Series) else [expr.sys2]
- den_term_1 = tf
- if isinstance(num, Series) and isinstance(expr.sys2, Series):
- den_term_2 = Series(*num_arg_list, *den_arg_list)
- elif isinstance(num, Series) and isinstance(expr.sys2, TransferFunction):
- if expr.sys2 == tf:
- den_term_2 = Series(*num_arg_list)
- else:
- den_term_2 = tf, Series(*num_arg_list, expr.sys2)
- elif isinstance(num, TransferFunction) and isinstance(expr.sys2, Series):
- if num == tf:
- den_term_2 = Series(*den_arg_list)
- else:
- den_term_2 = Series(num, *den_arg_list)
- else:
- if num == tf:
- den_term_2 = Series(*den_arg_list)
- elif expr.sys2 == tf:
- den_term_2 = Series(*num_arg_list)
- else:
- den_term_2 = Series(*num_arg_list, *den_arg_list)
- numer = self._print(num)
- denom_1 = self._print(den_term_1)
- denom_2 = self._print(den_term_2)
- _sign = "+" if expr.sign == -1 else "-"
- return r"\frac{%s}{%s %s %s}" % (numer, denom_1, _sign, denom_2)
- def _print_MIMOFeedback(self, expr):
- from sympy.physics.control import MIMOSeries
- inv_mat = self._print(MIMOSeries(expr.sys2, expr.sys1))
- sys1 = self._print(expr.sys1)
- _sign = "+" if expr.sign == -1 else "-"
- return r"\left(I_{\tau} %s %s\right)^{-1} \cdot %s" % (_sign, inv_mat, sys1)
- def _print_TransferFunctionMatrix(self, expr):
- mat = self._print(expr._expr_mat)
- return r"%s_\tau" % mat
- def _print_DFT(self, expr):
- return r"\text{{{}}}_{{{}}}".format(expr.__class__.__name__, expr.n)
- _print_IDFT = _print_DFT
- def _print_NamedMorphism(self, morphism):
- pretty_name = self._print(Symbol(morphism.name))
- pretty_morphism = self._print_Morphism(morphism)
- return "%s:%s" % (pretty_name, pretty_morphism)
- def _print_IdentityMorphism(self, morphism):
- from sympy.categories import NamedMorphism
- return self._print_NamedMorphism(NamedMorphism(
- morphism.domain, morphism.codomain, "id"))
- def _print_CompositeMorphism(self, morphism):
- # All components of the morphism have names and it is thus
- # possible to build the name of the composite.
- component_names_list = [self._print(Symbol(component.name)) for
- component in morphism.components]
- component_names_list.reverse()
- component_names = "\\circ ".join(component_names_list) + ":"
- pretty_morphism = self._print_Morphism(morphism)
- return component_names + pretty_morphism
- def _print_Category(self, morphism):
- return r"\mathbf{{{}}}".format(self._print(Symbol(morphism.name)))
- def _print_Diagram(self, diagram):
- if not diagram.premises:
- # This is an empty diagram.
- return self._print(S.EmptySet)
- latex_result = self._print(diagram.premises)
- if diagram.conclusions:
- latex_result += "\\Longrightarrow %s" % \
- self._print(diagram.conclusions)
- return latex_result
- def _print_DiagramGrid(self, grid):
- latex_result = "\\begin{array}{%s}\n" % ("c" * grid.width)
- for i in range(grid.height):
- for j in range(grid.width):
- if grid[i, j]:
- latex_result += latex(grid[i, j])
- latex_result += " "
- if j != grid.width - 1:
- latex_result += "& "
- if i != grid.height - 1:
- latex_result += "\\\\"
- latex_result += "\n"
- latex_result += "\\end{array}\n"
- return latex_result
- def _print_FreeModule(self, M):
- return '{{{}}}^{{{}}}'.format(self._print(M.ring), self._print(M.rank))
- def _print_FreeModuleElement(self, m):
- # Print as row vector for convenience, for now.
- return r"\left[ {} \right]".format(",".join(
- '{' + self._print(x) + '}' for x in m))
- def _print_SubModule(self, m):
- return r"\left\langle {} \right\rangle".format(",".join(
- '{' + self._print(x) + '}' for x in m.gens))
- def _print_ModuleImplementedIdeal(self, m):
- return r"\left\langle {} \right\rangle".format(",".join(
- '{' + self._print(x) + '}' for [x] in m._module.gens))
- def _print_Quaternion(self, expr):
- # TODO: This expression is potentially confusing,
- # shall we print it as `Quaternion( ... )`?
- s = [self.parenthesize(i, PRECEDENCE["Mul"], strict=True)
- for i in expr.args]
- a = [s[0]] + [i+" "+j for i, j in zip(s[1:], "ijk")]
- return " + ".join(a)
- def _print_QuotientRing(self, R):
- # TODO nicer fractions for few generators...
- return r"\frac{{{}}}{{{}}}".format(self._print(R.ring),
- self._print(R.base_ideal))
- def _print_QuotientRingElement(self, x):
- return r"{{{}}} + {{{}}}".format(self._print(x.data),
- self._print(x.ring.base_ideal))
- def _print_QuotientModuleElement(self, m):
- return r"{{{}}} + {{{}}}".format(self._print(m.data),
- self._print(m.module.killed_module))
- def _print_QuotientModule(self, M):
- # TODO nicer fractions for few generators...
- return r"\frac{{{}}}{{{}}}".format(self._print(M.base),
- self._print(M.killed_module))
- def _print_MatrixHomomorphism(self, h):
- return r"{{{}}} : {{{}}} \to {{{}}}".format(self._print(h._sympy_matrix()),
- self._print(h.domain), self._print(h.codomain))
- def _print_Manifold(self, manifold):
- string = manifold.name.name
- if '{' in string:
- name, supers, subs = string, [], []
- else:
- name, supers, subs = split_super_sub(string)
- name = translate(name)
- supers = [translate(sup) for sup in supers]
- subs = [translate(sub) for sub in subs]
- name = r'\text{%s}' % name
- if supers:
- name += "^{%s}" % " ".join(supers)
- if subs:
- name += "_{%s}" % " ".join(subs)
- return name
- def _print_Patch(self, patch):
- return r'\text{%s}_{%s}' % (self._print(patch.name), self._print(patch.manifold))
- def _print_CoordSystem(self, coordsys):
- return r'\text{%s}^{\text{%s}}_{%s}' % (
- self._print(coordsys.name), self._print(coordsys.patch.name), self._print(coordsys.manifold)
- )
- def _print_CovarDerivativeOp(self, cvd):
- return r'\mathbb{\nabla}_{%s}' % self._print(cvd._wrt)
- def _print_BaseScalarField(self, field):
- string = field._coord_sys.symbols[field._index].name
- return r'\mathbf{{{}}}'.format(self._print(Symbol(string)))
- def _print_BaseVectorField(self, field):
- string = field._coord_sys.symbols[field._index].name
- return r'\partial_{{{}}}'.format(self._print(Symbol(string)))
- def _print_Differential(self, diff):
- field = diff._form_field
- if hasattr(field, '_coord_sys'):
- string = field._coord_sys.symbols[field._index].name
- return r'\operatorname{{d}}{}'.format(self._print(Symbol(string)))
- else:
- string = self._print(field)
- return r'\operatorname{{d}}\left({}\right)'.format(string)
- def _print_Tr(self, p):
- # TODO: Handle indices
- contents = self._print(p.args[0])
- return r'\operatorname{{tr}}\left({}\right)'.format(contents)
- def _print_totient(self, expr, exp=None):
- if exp is not None:
- return r'\left(\phi\left(%s\right)\right)^{%s}' % \
- (self._print(expr.args[0]), exp)
- return r'\phi\left(%s\right)' % self._print(expr.args[0])
- def _print_reduced_totient(self, expr, exp=None):
- if exp is not None:
- return r'\left(\lambda\left(%s\right)\right)^{%s}' % \
- (self._print(expr.args[0]), exp)
- return r'\lambda\left(%s\right)' % self._print(expr.args[0])
- def _print_divisor_sigma(self, expr, exp=None):
- if len(expr.args) == 2:
- tex = r"_%s\left(%s\right)" % tuple(map(self._print,
- (expr.args[1], expr.args[0])))
- else:
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"\sigma^{%s}%s" % (exp, tex)
- return r"\sigma%s" % tex
- def _print_udivisor_sigma(self, expr, exp=None):
- if len(expr.args) == 2:
- tex = r"_%s\left(%s\right)" % tuple(map(self._print,
- (expr.args[1], expr.args[0])))
- else:
- tex = r"\left(%s\right)" % self._print(expr.args[0])
- if exp is not None:
- return r"\sigma^*^{%s}%s" % (exp, tex)
- return r"\sigma^*%s" % tex
- def _print_primenu(self, expr, exp=None):
- if exp is not None:
- return r'\left(\nu\left(%s\right)\right)^{%s}' % \
- (self._print(expr.args[0]), exp)
- return r'\nu\left(%s\right)' % self._print(expr.args[0])
- def _print_primeomega(self, expr, exp=None):
- if exp is not None:
- return r'\left(\Omega\left(%s\right)\right)^{%s}' % \
- (self._print(expr.args[0]), exp)
- return r'\Omega\left(%s\right)' % self._print(expr.args[0])
- def _print_Str(self, s):
- return str(s.name)
- def _print_float(self, expr):
- return self._print(Float(expr))
- def _print_int(self, expr):
- return str(expr)
- def _print_mpz(self, expr):
- return str(expr)
- def _print_mpq(self, expr):
- return str(expr)
- def _print_Predicate(self, expr):
- return r"\operatorname{{Q}}_{{\text{{{}}}}}".format(latex_escape(str(expr.name)))
- def _print_AppliedPredicate(self, expr):
- pred = expr.function
- args = expr.arguments
- pred_latex = self._print(pred)
- args_latex = ', '.join([self._print(a) for a in args])
- return '%s(%s)' % (pred_latex, args_latex)
- def emptyPrinter(self, expr):
- # default to just printing as monospace, like would normally be shown
- s = super().emptyPrinter(expr)
- return r"\mathtt{\text{%s}}" % latex_escape(s)
- def translate(s: str) -> str:
- r'''
- Check for a modifier ending the string. If present, convert the
- modifier to latex and translate the rest recursively.
- Given a description of a Greek letter or other special character,
- return the appropriate latex.
- Let everything else pass as given.
- >>> from sympy.printing.latex import translate
- >>> translate('alphahatdotprime')
- "{\\dot{\\hat{\\alpha}}}'"
- '''
- # Process the rest
- tex = tex_greek_dictionary.get(s)
- if tex:
- return tex
- elif s.lower() in greek_letters_set:
- return "\\" + s.lower()
- elif s in other_symbols:
- return "\\" + s
- else:
- # Process modifiers, if any, and recurse
- for key in sorted(modifier_dict.keys(), key=len, reverse=True):
- if s.lower().endswith(key) and len(s) > len(key):
- return modifier_dict[key](translate(s[:-len(key)]))
- return s
- @print_function(LatexPrinter)
- def latex(expr, **settings):
- r"""Convert the given expression to LaTeX string representation.
- Parameters
- ==========
- full_prec: boolean, optional
- If set to True, a floating point number is printed with full precision.
- fold_frac_powers : boolean, optional
- Emit ``^{p/q}`` instead of ``^{\frac{p}{q}}`` for fractional powers.
- fold_func_brackets : boolean, optional
- Fold function brackets where applicable.
- fold_short_frac : boolean, optional
- Emit ``p / q`` instead of ``\frac{p}{q}`` when the denominator is
- simple enough (at most two terms and no powers). The default value is
- ``True`` for inline mode, ``False`` otherwise.
- inv_trig_style : string, optional
- How inverse trig functions should be displayed. Can be one of
- ``'abbreviated'``, ``'full'``, or ``'power'``. Defaults to
- ``'abbreviated'``.
- itex : boolean, optional
- Specifies if itex-specific syntax is used, including emitting
- ``$$...$$``.
- ln_notation : boolean, optional
- If set to ``True``, ``\ln`` is used instead of default ``\log``.
- long_frac_ratio : float or None, optional
- The allowed ratio of the width of the numerator to the width of the
- denominator before the printer breaks off long fractions. If ``None``
- (the default value), long fractions are not broken up.
- mat_delim : string, optional
- The delimiter to wrap around matrices. Can be one of ``'['``, ``'('``,
- or the empty string ``''``. Defaults to ``'['``.
- mat_str : string, optional
- Which matrix environment string to emit. ``'smallmatrix'``,
- ``'matrix'``, ``'array'``, etc. Defaults to ``'smallmatrix'`` for
- inline mode, ``'matrix'`` for matrices of no more than 10 columns, and
- ``'array'`` otherwise.
- mode: string, optional
- Specifies how the generated code will be delimited. ``mode`` can be one
- of ``'plain'``, ``'inline'``, ``'equation'`` or ``'equation*'``. If
- ``mode`` is set to ``'plain'``, then the resulting code will not be
- delimited at all (this is the default). If ``mode`` is set to
- ``'inline'`` then inline LaTeX ``$...$`` will be used. If ``mode`` is
- set to ``'equation'`` or ``'equation*'``, the resulting code will be
- enclosed in the ``equation`` or ``equation*`` environment (remember to
- import ``amsmath`` for ``equation*``), unless the ``itex`` option is
- set. In the latter case, the ``$$...$$`` syntax is used.
- mul_symbol : string or None, optional
- The symbol to use for multiplication. Can be one of ``None``,
- ``'ldot'``, ``'dot'``, or ``'times'``.
- order: string, optional
- Any of the supported monomial orderings (currently ``'lex'``,
- ``'grlex'``, or ``'grevlex'``), ``'old'``, and ``'none'``. This
- parameter does nothing for `~.Mul` objects. Setting order to ``'old'``
- uses the compatibility ordering for ``~.Add`` defined in Printer. For
- very large expressions, set the ``order`` keyword to ``'none'`` if
- speed is a concern.
- symbol_names : dictionary of strings mapped to symbols, optional
- Dictionary of symbols and the custom strings they should be emitted as.
- root_notation : boolean, optional
- If set to ``False``, exponents of the form 1/n are printed in fractonal
- form. Default is ``True``, to print exponent in root form.
- mat_symbol_style : string, optional
- Can be either ``'plain'`` (default) or ``'bold'``. If set to
- ``'bold'``, a `~.MatrixSymbol` A will be printed as ``\mathbf{A}``,
- otherwise as ``A``.
- imaginary_unit : string, optional
- String to use for the imaginary unit. Defined options are ``'i'``
- (default) and ``'j'``. Adding ``r`` or ``t`` in front gives ``\mathrm``
- or ``\text``, so ``'ri'`` leads to ``\mathrm{i}`` which gives
- `\mathrm{i}`.
- gothic_re_im : boolean, optional
- If set to ``True``, `\Re` and `\Im` is used for ``re`` and ``im``, respectively.
- The default is ``False`` leading to `\operatorname{re}` and `\operatorname{im}`.
- decimal_separator : string, optional
- Specifies what separator to use to separate the whole and fractional parts of a
- floating point number as in `2.5` for the default, ``period`` or `2{,}5`
- when ``comma`` is specified. Lists, sets, and tuple are printed with semicolon
- separating the elements when ``comma`` is chosen. For example, [1; 2; 3] when
- ``comma`` is chosen and [1,2,3] for when ``period`` is chosen.
- parenthesize_super : boolean, optional
- If set to ``False``, superscripted expressions will not be parenthesized when
- powered. Default is ``True``, which parenthesizes the expression when powered.
- min: Integer or None, optional
- Sets the lower bound for the exponent to print floating point numbers in
- fixed-point format.
- max: Integer or None, optional
- Sets the upper bound for the exponent to print floating point numbers in
- fixed-point format.
- diff_operator: string, optional
- String to use for differential operator. Default is ``'d'``, to print in italic
- form. ``'rd'``, ``'td'`` are shortcuts for ``\mathrm{d}`` and ``\text{d}``.
- Notes
- =====
- Not using a print statement for printing, results in double backslashes for
- latex commands since that's the way Python escapes backslashes in strings.
- >>> from sympy import latex, Rational
- >>> from sympy.abc import tau
- >>> latex((2*tau)**Rational(7,2))
- '8 \\sqrt{2} \\tau^{\\frac{7}{2}}'
- >>> print(latex((2*tau)**Rational(7,2)))
- 8 \sqrt{2} \tau^{\frac{7}{2}}
- Examples
- ========
- >>> from sympy import latex, pi, sin, asin, Integral, Matrix, Rational, log
- >>> from sympy.abc import x, y, mu, r, tau
- Basic usage:
- >>> print(latex((2*tau)**Rational(7,2)))
- 8 \sqrt{2} \tau^{\frac{7}{2}}
- ``mode`` and ``itex`` options:
- >>> print(latex((2*mu)**Rational(7,2), mode='plain'))
- 8 \sqrt{2} \mu^{\frac{7}{2}}
- >>> print(latex((2*tau)**Rational(7,2), mode='inline'))
- $8 \sqrt{2} \tau^{7 / 2}$
- >>> print(latex((2*mu)**Rational(7,2), mode='equation*'))
- \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*}
- >>> print(latex((2*mu)**Rational(7,2), mode='equation'))
- \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation}
- >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True))
- $$8 \sqrt{2} \mu^{\frac{7}{2}}$$
- >>> print(latex((2*mu)**Rational(7,2), mode='plain'))
- 8 \sqrt{2} \mu^{\frac{7}{2}}
- >>> print(latex((2*tau)**Rational(7,2), mode='inline'))
- $8 \sqrt{2} \tau^{7 / 2}$
- >>> print(latex((2*mu)**Rational(7,2), mode='equation*'))
- \begin{equation*}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation*}
- >>> print(latex((2*mu)**Rational(7,2), mode='equation'))
- \begin{equation}8 \sqrt{2} \mu^{\frac{7}{2}}\end{equation}
- >>> print(latex((2*mu)**Rational(7,2), mode='equation', itex=True))
- $$8 \sqrt{2} \mu^{\frac{7}{2}}$$
- Fraction options:
- >>> print(latex((2*tau)**Rational(7,2), fold_frac_powers=True))
- 8 \sqrt{2} \tau^{7/2}
- >>> print(latex((2*tau)**sin(Rational(7,2))))
- \left(2 \tau\right)^{\sin{\left(\frac{7}{2} \right)}}
- >>> print(latex((2*tau)**sin(Rational(7,2)), fold_func_brackets=True))
- \left(2 \tau\right)^{\sin {\frac{7}{2}}}
- >>> print(latex(3*x**2/y))
- \frac{3 x^{2}}{y}
- >>> print(latex(3*x**2/y, fold_short_frac=True))
- 3 x^{2} / y
- >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=2))
- \frac{\int r\, dr}{2 \pi}
- >>> print(latex(Integral(r, r)/2/pi, long_frac_ratio=0))
- \frac{1}{2 \pi} \int r\, dr
- Multiplication options:
- >>> print(latex((2*tau)**sin(Rational(7,2)), mul_symbol="times"))
- \left(2 \times \tau\right)^{\sin{\left(\frac{7}{2} \right)}}
- Trig options:
- >>> print(latex(asin(Rational(7,2))))
- \operatorname{asin}{\left(\frac{7}{2} \right)}
- >>> print(latex(asin(Rational(7,2)), inv_trig_style="full"))
- \arcsin{\left(\frac{7}{2} \right)}
- >>> print(latex(asin(Rational(7,2)), inv_trig_style="power"))
- \sin^{-1}{\left(\frac{7}{2} \right)}
- Matrix options:
- >>> print(latex(Matrix(2, 1, [x, y])))
- \left[\begin{matrix}x\\y\end{matrix}\right]
- >>> print(latex(Matrix(2, 1, [x, y]), mat_str = "array"))
- \left[\begin{array}{c}x\\y\end{array}\right]
- >>> print(latex(Matrix(2, 1, [x, y]), mat_delim="("))
- \left(\begin{matrix}x\\y\end{matrix}\right)
- Custom printing of symbols:
- >>> print(latex(x**2, symbol_names={x: 'x_i'}))
- x_i^{2}
- Logarithms:
- >>> print(latex(log(10)))
- \log{\left(10 \right)}
- >>> print(latex(log(10), ln_notation=True))
- \ln{\left(10 \right)}
- ``latex()`` also supports the builtin container types :class:`list`,
- :class:`tuple`, and :class:`dict`:
- >>> print(latex([2/x, y], mode='inline'))
- $\left[ 2 / x, \ y\right]$
- Unsupported types are rendered as monospaced plaintext:
- >>> print(latex(int))
- \mathtt{\text{<class 'int'>}}
- >>> print(latex("plain % text"))
- \mathtt{\text{plain \% text}}
- See :ref:`printer_method_example` for an example of how to override
- this behavior for your own types by implementing ``_latex``.
- .. versionchanged:: 1.7.0
- Unsupported types no longer have their ``str`` representation treated as valid latex.
- """
- return LatexPrinter(settings).doprint(expr)
- def print_latex(expr, **settings):
- """Prints LaTeX representation of the given expression. Takes the same
- settings as ``latex()``."""
- print(latex(expr, **settings))
- def multiline_latex(lhs, rhs, terms_per_line=1, environment="align*", use_dots=False, **settings):
- r"""
- This function generates a LaTeX equation with a multiline right-hand side
- in an ``align*``, ``eqnarray`` or ``IEEEeqnarray`` environment.
- Parameters
- ==========
- lhs : Expr
- Left-hand side of equation
- rhs : Expr
- Right-hand side of equation
- terms_per_line : integer, optional
- Number of terms per line to print. Default is 1.
- environment : "string", optional
- Which LaTeX wnvironment to use for the output. Options are "align*"
- (default), "eqnarray", and "IEEEeqnarray".
- use_dots : boolean, optional
- If ``True``, ``\\dots`` is added to the end of each line. Default is ``False``.
- Examples
- ========
- >>> from sympy import multiline_latex, symbols, sin, cos, exp, log, I
- >>> x, y, alpha = symbols('x y alpha')
- >>> expr = sin(alpha*y) + exp(I*alpha) - cos(log(y))
- >>> print(multiline_latex(x, expr))
- \begin{align*}
- x = & e^{i \alpha} \\
- & + \sin{\left(\alpha y \right)} \\
- & - \cos{\left(\log{\left(y \right)} \right)}
- \end{align*}
- Using at most two terms per line:
- >>> print(multiline_latex(x, expr, 2))
- \begin{align*}
- x = & e^{i \alpha} + \sin{\left(\alpha y \right)} \\
- & - \cos{\left(\log{\left(y \right)} \right)}
- \end{align*}
- Using ``eqnarray`` and dots:
- >>> print(multiline_latex(x, expr, terms_per_line=2, environment="eqnarray", use_dots=True))
- \begin{eqnarray}
- x & = & e^{i \alpha} + \sin{\left(\alpha y \right)} \dots\nonumber\\
- & & - \cos{\left(\log{\left(y \right)} \right)}
- \end{eqnarray}
- Using ``IEEEeqnarray``:
- >>> print(multiline_latex(x, expr, environment="IEEEeqnarray"))
- \begin{IEEEeqnarray}{rCl}
- x & = & e^{i \alpha} \nonumber\\
- & & + \sin{\left(\alpha y \right)} \nonumber\\
- & & - \cos{\left(\log{\left(y \right)} \right)}
- \end{IEEEeqnarray}
- Notes
- =====
- All optional parameters from ``latex`` can also be used.
- """
- # Based on code from https://github.com/sympy/sympy/issues/3001
- l = LatexPrinter(**settings)
- if environment == "eqnarray":
- result = r'\begin{eqnarray}' + '\n'
- first_term = '& = &'
- nonumber = r'\nonumber'
- end_term = '\n\\end{eqnarray}'
- doubleet = True
- elif environment == "IEEEeqnarray":
- result = r'\begin{IEEEeqnarray}{rCl}' + '\n'
- first_term = '& = &'
- nonumber = r'\nonumber'
- end_term = '\n\\end{IEEEeqnarray}'
- doubleet = True
- elif environment == "align*":
- result = r'\begin{align*}' + '\n'
- first_term = '= &'
- nonumber = ''
- end_term = '\n\\end{align*}'
- doubleet = False
- else:
- raise ValueError("Unknown environment: {}".format(environment))
- dots = ''
- if use_dots:
- dots=r'\dots'
- terms = rhs.as_ordered_terms()
- n_terms = len(terms)
- term_count = 1
- for i in range(n_terms):
- term = terms[i]
- term_start = ''
- term_end = ''
- sign = '+'
- if term_count > terms_per_line:
- if doubleet:
- term_start = '& & '
- else:
- term_start = '& '
- term_count = 1
- if term_count == terms_per_line:
- # End of line
- if i < n_terms-1:
- # There are terms remaining
- term_end = dots + nonumber + r'\\' + '\n'
- else:
- term_end = ''
- if term.as_ordered_factors()[0] == -1:
- term = -1*term
- sign = r'-'
- if i == 0: # beginning
- if sign == '+':
- sign = ''
- result += r'{:s} {:s}{:s} {:s} {:s}'.format(l.doprint(lhs),
- first_term, sign, l.doprint(term), term_end)
- else:
- result += r'{:s}{:s} {:s} {:s}'.format(term_start, sign,
- l.doprint(term), term_end)
- term_count += 1
- result += end_term
- return result
|