def _print_latex_matplotlib(o): """ A function that returns a png rendered by mathtext """ debug("_print_latex_matplotlib:", "called with %s" % o) if _can_print_latex(o): s = latex(o, mode='inline') return _matplotlib_wrapper(s)
def _print_latex_text(o): debug("_print_latex_text:", "called with %s" % o) """ A function to generate the latex representation of sympy expressions. """ if _can_print_latex(o): s = latex(o, mode='plain') s = s.replace(r'\dag', r'\dagger') s = s.strip('$') debug("_print_latex_text:", "returns $$%s$$" % s) return '$$%s$$' % s
def _preview_wrapper(o): exprbuffer = BytesIO() try: preview( o, output="png", viewer="BytesIO", outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions ) except Exception as e: # IPython swallows exceptions debug("png printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue()
def _matplotlib_wrapper(o): debug("_matplotlib_wrapper:", "called with %s" % o) # mathtext does not understand certain latex flags, so we try to # replace them with suitable subs o = o.replace(r'\operatorname', '') o = o.replace(r'\overline', r'\bar') try: return latex_to_png(o) except Exception: debug("_matplotlib_wrapper:", "exeption raised") # Matplotlib.mathtext cannot render some things (like # matrices) return None
def _matplotlib_wrapper(o): # mathtext does not understand certain latex flags, so we try to # replace them with suitable subs o = o.replace(r'\operatorname', '') o = o.replace(r'\overline', r'\bar') # mathtext can't render some LaTeX commands. For example, it can't # render any LaTeX environments such as array or matrix. So here we # ensure that if mathtext fails to render, we return None. try: return latex_to_png(o) except ValueError as e: debug('matplotlib exception caught:', repr(e)) return None
def downvalues_rules(r, parsed): ''' Function which generates parsed rules by substituting all possible combinations of default values. ''' res = [] index = 0 for i in r: debug('parsing rule {}'.format(r.index(i) + 1)) # Parse Pattern if i[1][1][0] == 'Condition': p = i[1][1][1].copy() else: p = i[1][1].copy() optional = get_default_values(p, {}) pattern = generate_sympy_from_parsed(p.copy(), replace_Int=True) pattern, free_symbols = add_wildcards(pattern, optional=optional) free_symbols = list(set(free_symbols)) #remove common symbols # Parse Transformed Expression and Constraints if i[2][0] == 'Condition': # parse rules without constraints separately constriant = divide_constraint(i[2][2], free_symbols) # separate And constraints into individual constraints FreeQ_vars, FreeQ_x = seperate_freeq(i[2][2].copy()) # separate FreeQ into individual constraints transformed = generate_sympy_from_parsed(i[2][1].copy(), symbols=free_symbols) else: constriant = '' FreeQ_vars, FreeQ_x = [], [] transformed = generate_sympy_from_parsed(i[2].copy(), symbols=free_symbols) FreeQ_constraint = parse_freeq(FreeQ_vars, FreeQ_x, free_symbols) pattern = sympify(pattern) pattern = rubi_printer(pattern, sympy_integers=True) pattern = setWC(pattern) transformed = sympify(transformed) index += 1 if type(transformed) == Function('With') or type(transformed) == Function('Module'): # define separate function when With appears transformed, With_constraints = replaceWith(transformed, free_symbols, index) parsed += ' pattern' + str(index) +' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + With_constraints + ')' parsed += '\n{}'.format(transformed) parsed += '\n ' + 'rule' + str(index) +' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', lambda ' + ', '.join(free_symbols) + ' : ' + 'With{}({})'.format(index, ', '.join(free_symbols)) + ')\n ' else: transformed = rubi_printer(transformed, sympy_integers=True) parsed += ' pattern' + str(index) +' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + ')' parsed += '\n ' + 'rule' + str(index) +' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', lambda ' + ', '.join(free_symbols) + ' : ' + transformed + ')\n ' parsed += 'rubi.add(rule'+ str(index) +')\n\n' parsed += ' return rubi\n' return parsed
def _print_latex_png(o): """ A function that returns a png rendered by an external latex distribution, falling back to matplotlib rendering """ if _can_print_latex(o): s = latex(o, mode=latex_mode) try: return _preview_wrapper(s) except RuntimeError as e: debug('preview failed with:', repr(e), ' Falling back to matplotlib backend') if latex_mode != 'inline': s = latex(o, mode='inline') return _matplotlib_wrapper(s)
def _print_latex_png(o): debug("_print_latex_png:", "called with %s" % o) """ A function that returns a png rendered by an external latex distribution, falling back to matplotlib rendering """ if _can_print_latex(o): s = latex(o, mode=latex_mode) try: return _preview_wrapper(s) except RuntimeError: if latex_mode != 'inline': s = latex(o, mode='inline') debug("_print_latex_png(o):", "calling _matplotlib_wrapper") return _matplotlib_wrapper(s)
def param_rischDE(fa, fd, G, DE): """ Solve a Parametric Risch Differential Equation: Dy + f*y == Sum(ci*Gi, (i, 1, m)). """ _, (fa, fd) = weak_normalizer(fa, fd, DE) a, (ba, bd), G, hn = prde_normal_denom(ga, gd, G, DE) A, B, G, hs = prde_special_denom(a, ba, bd, G, DE) g = gcd(A, B) A, B, G = A.quo(g), B.quo(g), [gia.cancel(gid*g, include=True) for gia, gid in G] Q, M = prde_linear_constraints(A, B, G, DE) M, _ = constant_system(M, zeros(M.rows, 1), DE) # Reduce number of constants at this point try: # Similar to rischDE(), we try oo, even though it might lead to # non-termination when there is no solution. At least for prde_spde, # it will always terminate no matter what n is. n = bound_degree(A, B, G, DE, parametric=True) except NotImplementedError: debug("param_rischDE: Proceeding with n = oo; may cause " "non-termination.") n = oo A, B, Q, R, n1 = prde_spde(A, B, Q, n, DE)
def build_parser(output_dir=dir_latex_antlr): check_antlr_version() debug("Updating ANTLR-generated code in {}".format(output_dir)) if not os.path.exists(output_dir): os.makedirs(output_dir) with open(os.path.join(output_dir, "__init__.py"), "w+") as fp: fp.write(header) args = [ "antlr4", grammar_file, "-o", output_dir, # for now, not generating these as latex2sympy did not use them "-no-visitor", "-no-listener", ] debug("Running code generation...\n\t$ {}".format(" ".join(args))) subprocess.check_output(args, cwd=output_dir) debug("Applying headers and renaming...") # Handle case insensitive file systems. If the files are already # generated, they will be written to latex* but LaTeX*.* won't match them. for path in (glob.glob(os.path.join(output_dir, "LaTeX*.*")) + glob.glob(os.path.join(output_dir, "latex*.*"))): offset = 0 new_path = os.path.join(output_dir, os.path.basename(path).lower()) with open(path, 'r') as f: lines = [line.rstrip() + '\n' for line in f.readlines()] os.unlink(path) with open(new_path, "w") as out_file: if path.endswith(".py"): offset = 2 out_file.write(header) out_file.writelines(lines[offset:]) debug("\t{}".format(new_path)) return True
def check_antlr_version(): debug("Checking antlr4 version...") try: debug(subprocess.check_output(["antlr4"]) .decode('utf-8').split("\n")[0]) return True except (subprocess.CalledProcessError, FileNotFoundError): debug("The antlr4 command line tool is not installed, " "or not on your PATH\n" "> Please install it via your preferred package manager") return False
def downvalues_rules(r, header, cons_dict, cons_index, index): ''' Function which generates parsed rules by substituting all possible combinations of default values. ''' rules = '[' parsed = '\n\n' cons = '' cons_import = [] # it contains name of constraints that need to be imported for rules. for i in r: debug('parsing rule {}'.format(r.index(i) + 1)) # Parse Pattern if i[1][1][0] == 'Condition': p = i[1][1][1].copy() else: p = i[1][1].copy() optional = get_default_values(p, {}) pattern = generate_sympy_from_parsed(p.copy(), replace_Int=True) pattern, free_symbols = add_wildcards(pattern, optional=optional) free_symbols = list(set(free_symbols)) #remove common symbols # Parse Transformed Expression and Constraints if i[2][0] == 'Condition': # parse rules without constraints separately constriant, constraint_def, cons_index = divide_constraint(i[2][2], free_symbols, cons_index, cons_dict, cons_import) # separate And constraints into individual constraints FreeQ_vars, FreeQ_x = seperate_freeq(i[2][2].copy()) # separate FreeQ into individual constraints transformed = generate_sympy_from_parsed(i[2][1].copy(), symbols=free_symbols) else: constriant = '' constraint_def = '' FreeQ_vars, FreeQ_x = [], [] transformed = generate_sympy_from_parsed(i[2].copy(), symbols=free_symbols) FreeQ_constraint, free_cons_def, cons_index = parse_freeq(FreeQ_vars, FreeQ_x, cons_index, cons_dict, cons_import, free_symbols) pattern = sympify(pattern, locals={"Or": Function("Or"), "And": Function("And"), "Not":Function("Not") }) pattern = rubi_printer(pattern, sympy_integers=True) pattern = setWC(pattern) transformed = sympify(transformed, locals={"Or": Function("Or"), "And": Function("And"), "Not":Function("Not") }) constraint_def = constraint_def + free_cons_def cons+=constraint_def index += 1 # below are certain if - else condition depending on various situation that may be encountered if type(transformed) == Function('With') or type(transformed) == Function('Module'): # define separate function when With appears transformed, With_constraints, return_type = replaceWith(transformed, free_symbols, index) if return_type is None: parsed += '{}'.format(transformed) parsed += '\n pattern' + str(index) +' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + ')' parsed += '\n ' + 'rule' + str(index) +' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', With{}'.format(index) + ')\n' else: parsed += '{}'.format(transformed) parsed += '\n pattern' + str(index) +' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + With_constraints + ')' parsed += '\n def replacement{}({}):\n'.format(index, ', '.join(free_symbols)) + return_type[0] + '\n rubi.append({})\n return '.format(index) + return_type[1] parsed += '\n ' + 'rule' + str(index) +' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', replacement{}'.format(index) + ')\n' else: transformed = rubi_printer(transformed, sympy_integers=True) parsed += ' pattern' + str(index) +' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + ')' parsed += '\n def replacement{}({}):\n rubi.append({})\n return '.format(index, ', '.join(free_symbols), index) + transformed parsed += '\n ' + 'rule' + str(index) +' = ReplacementRule(' + 'pattern' + rubi_printer(index, sympy_integers=True) + ', replacement{}'.format(index) + ')\n' rules += 'rule{}, '.format(index) rules += ']' parsed += ' return ' + rules +'\n' header += ' from sympy.integrals.rubi.constraints import ' + ', '.join(word for word in cons_import) parsed = header + parsed return parsed, cons_index, cons, index
def _init_ipython_printing( ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode, print_builtin ): """Setup printing in IPython interactive session. """ try: from IPython.lib.latextools import latex_to_png except ImportError: pass preamble = ( "\\documentclass[%s]{article}\n" "\\pagestyle{empty}\n" "\\usepackage{amsmath,amsfonts}%s\\begin{document}" ) if euler: addpackages = "\\usepackage{euler}" else: addpackages = "" preamble = preamble % (fontsize, addpackages) imagesize = "tight" offset = "0cm,0cm" resolution = 150 dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % (imagesize, resolution, backcolor, forecolor, offset) dvioptions = dvi.split() debug("init_printing: DVIOPTIONS:", dvioptions) debug("init_printing: PREAMBLE:", preamble) def _print_plain(arg, p, cycle): """caller for pretty, for use in IPython 0.11""" if _can_print_latex(arg): p.text(stringify_func(arg)) else: p.text(IPython.lib.pretty.pretty(arg)) def _preview_wrapper(o): exprbuffer = BytesIO() try: preview( o, output="png", viewer="BytesIO", outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions ) except Exception as e: # IPython swallows exceptions debug("png printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue() def _matplotlib_wrapper(o): # mathtext does not understand centain latex flags, so we try to # replace them with suitable subs o = o.replace(r"\operatorname", "") o = o.replace(r"\overline", r"\bar") return latex_to_png(o) def _can_print_latex(o): """Return True if type o can be printed with LaTeX. If o is a container type, this is True if and only if every element of o can be printed with LaTeX. """ import sympy if isinstance(o, (list, tuple, set, frozenset)): return all(_can_print_latex(i) for i in o) elif isinstance(o, dict): return all(_can_print_latex(i) and _can_print_latex(o[i]) for i in o) elif isinstance(o, bool): return False elif isinstance(o, (sympy.Basic, sympy.matrices.MatrixBase, Vector, Dyadic)): return True elif isinstance(o, (float, integer_types)) and print_builtin: return True return False def _print_latex_png(o): """ A function that returns a png rendered by an external latex distribution, falling back to matplotlib rendering """ if _can_print_latex(o): s = latex(o, mode=latex_mode) try: return _preview_wrapper(s) except RuntimeError: if latex_mode != "inline": s = latex(o, mode="inline") return _matplotlib_wrapper(s) def _print_latex_matplotlib(o): """ A function that returns a png rendered by mathtext """ if _can_print_latex(o): s = latex(o, mode="inline") return _matplotlib_wrapper(s) def _print_latex_text(o): """ A function to generate the latex representation of sympy expressions. """ if _can_print_latex(o): s = latex(o, mode="plain") s = s.replace(r"\dag", r"\dagger") s = s.strip("$") return "$$%s$$" % s def _result_display(self, arg): """IPython's pretty-printer display hook, for use in IPython 0.10 This function was adapted from: ipython/IPython/hooks.py:155 """ if self.rc.pprint: out = stringify_func(arg) if "\n" in out: print print(out) else: print(repr(arg)) import IPython if IPython.__version__ >= "0.11": from sympy.core.basic import Basic from sympy.matrices.matrices import MatrixBase printable_types = [Basic, MatrixBase, float, tuple, list, set, frozenset, dict, Vector, Dyadic] + list( integer_types ) plaintext_formatter = ip.display_formatter.formatters["text/plain"] for cls in printable_types: plaintext_formatter.for_type(cls, _print_plain) png_formatter = ip.display_formatter.formatters["image/png"] if use_latex in (True, "png"): debug("init_printing: using png formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_png) elif use_latex == "matplotlib": debug("init_printing: using matplotlib formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_matplotlib) else: debug("init_printing: not using any png formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython # png_formatter.for_type(cls, None) if cls in png_formatter.type_printers: png_formatter.type_printers.pop(cls) latex_formatter = ip.display_formatter.formatters["text/latex"] if use_latex in (True, "mathjax"): debug("init_printing: using mathjax formatter") for cls in printable_types: latex_formatter.for_type(cls, _print_latex_text) else: debug("init_printing: not using text/latex formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython # latex_formatter.for_type(cls, None) if cls in latex_formatter.type_printers: latex_formatter.type_printers.pop(cls) else: ip.set_hook("result_display", _result_display)
def init_printing(pretty_print=True, order=None, use_unicode=None, use_latex=None, wrap_line=None, num_columns=None, no_global=False, ip=None, euler=False, forecolor='Black', backcolor='Transparent', fontsize='10pt', latex_mode='equation*'): """ Initializes pretty-printer depending on the environment. Parameters ========== pretty_print: boolean If True, use pretty_print to stringify; if False, use sstrrepr to stringify. order: string or None There are a few different settings for this parameter: lex (default), which is lexographic order; grlex, which is graded lexographic order; grevlex, which is reversed graded lexographic order; old, which is used for compatibility reasons and for long expressions; None, which sets it to lex. use_unicode: boolean or None If True, use unicode characters; if False, do not use unicode characters. use_latex: string, boolean, or None If True, use default latex rendering in GUI interfaces (png and mathjax); if False, do not use latex rendering; if 'png', enable latex rendering with an external latex compiler, falling back to matplotlib if external compilation fails; if 'matplotlib', enable latex rendering with matplotlib; if 'mathjax', enable latex text generation, for example MathJax rendering in IPython notebook or text rendering in LaTeX documents wrap_line: boolean If True, lines will wrap at the end; if False, they will not wrap but continue as one line. num_columns: int or None If int, number of columns before wrapping is set to num_columns; if None, number of columns before wrapping is set to terminal width. no_global: boolean If True, the settings become system wide; if False, use just for this console/session. ip: An interactive console This can either be an instance of IPython, or a class that derives from code.InteractiveConsole. Examples ======== >>> from sympy.interactive import init_printing >>> from sympy import Symbol, sqrt >>> from sympy.abc import x, y >>> sqrt(5) sqrt(5) >>> init_printing(pretty_print=True) # doctest: +SKIP >>> sqrt(5) # doctest: +SKIP ___ \/ 5 >>> theta = Symbol('theta') # doctest: +SKIP >>> init_printing(use_unicode=True) # doctest: +SKIP >>> theta # doctest: +SKIP \u03b8 >>> init_printing(use_unicode=False) # doctest: +SKIP >>> theta # doctest: +SKIP theta >>> init_printing(order='lex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grlex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grevlex') # doctest: +SKIP >>> str(y * x**2 + x * y**2) # doctest: +SKIP x**2*y + x*y**2 >>> init_printing(order='old') # doctest: +SKIP >>> str(x**2 + y**2 + x + y) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(num_columns=10) # doctest: +SKIP >>> x**2 + x + y**2 + y # doctest: +SKIP x + y + x**2 + y**2 """ import sys from sympy.printing.printer import Printer if pretty_print: from sympy.printing import pretty as stringify_func else: from sympy.printing import sstrrepr as stringify_func # Even if ip is not passed, double check that not in IPython shell if ip is None: try: ip = get_ipython() except NameError: pass if ip and pretty_print: try: import IPython # IPython 1.0 deprecates the frontend module, so we import directly # from the terminal module to prevent a deprecation message from being # shown. if IPython.__version__ >= '1.0': from IPython.terminal.interactiveshell import TerminalInteractiveShell else: from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from code import InteractiveConsole except ImportError: pass else: # This will be True if we are in the qtconsole or notebook if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \ and 'ipython-console' not in ''.join(sys.argv): if use_unicode is None: debug("init_printing: Setting use_unicode to True") use_unicode = True if use_latex is None: debug("init_printing: Setting use_latex to True") use_latex = True if not no_global: Printer.set_global_settings(order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: _stringify_func = stringify_func if pretty_print: stringify_func = lambda expr: \ _stringify_func(expr, order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: stringify_func = lambda expr: _stringify_func(expr, order=order) if ip is not None and ip.__module__.startswith('IPython'): _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode) else: _init_python_printing(stringify_func)
def init_printing(pretty_print=True, order=None, use_unicode=None, use_latex=None, wrap_line=None, num_columns=None, no_global=False, ip=None, euler=False, forecolor='Black', backcolor='Transparent', fontsize='10pt', latex_mode='equation*', print_builtin=True, str_printer=None, pretty_printer=None, latex_printer=None, **settings): r""" Initializes pretty-printer depending on the environment. Parameters ========== pretty_print: boolean If True, use pretty_print to stringify or the provided pretty printer; if False, use sstrrepr to stringify or the provided string printer. order: string or None There are a few different settings for this parameter: lex (default), which is lexographic order; grlex, which is graded lexographic order; grevlex, which is reversed graded lexographic order; old, which is used for compatibility reasons and for long expressions; None, which sets it to lex. use_unicode: boolean or None If True, use unicode characters; if False, do not use unicode characters. use_latex: string, boolean, or None If True, use default latex rendering in GUI interfaces (png and mathjax); if False, do not use latex rendering; if 'png', enable latex rendering with an external latex compiler, falling back to matplotlib if external compilation fails; if 'matplotlib', enable latex rendering with matplotlib; if 'mathjax', enable latex text generation, for example MathJax rendering in IPython notebook or text rendering in LaTeX documents wrap_line: boolean If True, lines will wrap at the end; if False, they will not wrap but continue as one line. This is only relevant if `pretty_print` is True. num_columns: int or None If int, number of columns before wrapping is set to num_columns; if None, number of columns before wrapping is set to terminal width. This is only relevant if `pretty_print` is True. no_global: boolean If True, the settings become system wide; if False, use just for this console/session. ip: An interactive console This can either be an instance of IPython, or a class that derives from code.InteractiveConsole. euler: boolean, optional, default=False Loads the euler package in the LaTeX preamble for handwritten style fonts (http://www.ctan.org/pkg/euler). forecolor: string, optional, default='Black' DVI setting for foreground color. backcolor: string, optional, default='Transparent' DVI setting for background color. fontsize: string, optional, default='10pt' A font size to pass to the LaTeX documentclass function in the preamble. latex_mode: string, optional, default='equation*' The mode used in the LaTeX printer. Can be one of: {'inline'|'plain'|'equation'|'equation*'}. print_builtin: boolean, optional, default=True If true then floats and integers will be printed. If false the printer will only print SymPy types. str_printer: function, optional, default=None A custom string printer function. This should mimic sympy.printing.sstrrepr(). pretty_printer: function, optional, default=None A custom pretty printer. This should mimic sympy.printing.pretty(). latex_printer: function, optional, default=None A custom LaTeX printer. This should mimic sympy.printing.latex(). Examples ======== >>> from sympy.interactive import init_printing >>> from sympy import Symbol, sqrt >>> from sympy.abc import x, y >>> sqrt(5) sqrt(5) >>> init_printing(pretty_print=True) # doctest: +SKIP >>> sqrt(5) # doctest: +SKIP ___ \/ 5 >>> theta = Symbol('theta') # doctest: +SKIP >>> init_printing(use_unicode=True) # doctest: +SKIP >>> theta # doctest: +SKIP \u03b8 >>> init_printing(use_unicode=False) # doctest: +SKIP >>> theta # doctest: +SKIP theta >>> init_printing(order='lex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grlex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grevlex') # doctest: +SKIP >>> str(y * x**2 + x * y**2) # doctest: +SKIP x**2*y + x*y**2 >>> init_printing(order='old') # doctest: +SKIP >>> str(x**2 + y**2 + x + y) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(num_columns=10) # doctest: +SKIP >>> x**2 + x + y**2 + y # doctest: +SKIP x + y + x**2 + y**2 """ import sys from sympy.printing.printer import Printer if pretty_print: if pretty_printer is not None: stringify_func = pretty_printer else: from sympy.printing import pretty as stringify_func else: if str_printer is not None: stringify_func = str_printer else: from sympy.printing import sstrrepr as stringify_func # Even if ip is not passed, double check that not in IPython shell in_ipython = False if ip is None: try: ip = get_ipython() except NameError: pass else: in_ipython = (ip is not None) if ip and not in_ipython: in_ipython = _is_ipython(ip) if in_ipython and pretty_print: try: import IPython # IPython 1.0 deprecates the frontend module, so we import directly # from the terminal module to prevent a deprecation message from being # shown. if V(IPython.__version__) >= '1.0': from IPython.terminal.interactiveshell import TerminalInteractiveShell else: from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from code import InteractiveConsole except ImportError: pass else: # This will be True if we are in the qtconsole or notebook if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \ and 'ipython-console' not in ''.join(sys.argv): if use_unicode is None: debug("init_printing: Setting use_unicode to True") use_unicode = True if use_latex is None: debug("init_printing: Setting use_latex to True") use_latex = True if not NO_GLOBAL and not no_global: Printer.set_global_settings(order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: _stringify_func = stringify_func if pretty_print: stringify_func = lambda expr: \ _stringify_func(expr, order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: stringify_func = lambda expr: _stringify_func(expr, order=order) if in_ipython: mode_in_settings = settings.pop("mode", None) if mode_in_settings: debug("init_printing: Mode is not able to be set due to internals" "of IPython printing") _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode, print_builtin, latex_printer, **settings) else: _init_python_printing(stringify_func, **settings)
def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode, print_builtin, latex_printer): """Setup printing in IPython interactive session. """ try: from IPython.lib.latextools import latex_to_png except ImportError: pass preamble = "\\documentclass[%s]{article}\n" \ "\\pagestyle{empty}\n" \ "\\usepackage{amsmath,amsfonts}%s\\begin{document}" if euler: addpackages = '\\usepackage{euler}' else: addpackages = '' preamble = preamble % (fontsize, addpackages) imagesize = 'tight' offset = "0cm,0cm" resolution = 150 dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % (imagesize, resolution, backcolor, forecolor, offset) dvioptions = dvi.split() debug("init_printing: DVIOPTIONS:", dvioptions) debug("init_printing: PREAMBLE:", preamble) latex = latex_printer or default_latex def _print_plain(arg, p, cycle): """caller for pretty, for use in IPython 0.11""" if _can_print_latex(arg): p.text(stringify_func(arg)) else: p.text(IPython.lib.pretty.pretty(arg)) def _preview_wrapper(o): exprbuffer = BytesIO() try: preview(o, output='png', viewer='BytesIO', outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions) except Exception as e: # IPython swallows exceptions debug("png printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue() def _matplotlib_wrapper(o): # mathtext does not understand certain latex flags, so we try to # replace them with suitable subs o = o.replace(r'\operatorname', '') o = o.replace(r'\overline', r'\bar') # mathtext can't render some LaTeX commands. For example, it can't # render any LaTeX environments such as array or matrix. So here we # ensure that if mathtext fails to render, we return None. try: return latex_to_png(o) except ValueError as e: debug('matplotlib exception caught:', repr(e)) return None def _can_print_latex(o): """Return True if type o can be printed with LaTeX. If o is a container type, this is True if and only if every element of o can be printed with LaTeX. """ from sympy import Basic from sympy.matrices import MatrixBase from sympy.physics.vector import Vector, Dyadic if isinstance(o, (list, tuple, set, frozenset)): return all(_can_print_latex(i) for i in o) elif isinstance(o, dict): return all( _can_print_latex(i) and _can_print_latex(o[i]) for i in o) elif isinstance(o, bool): return False # TODO : Investigate if "elif hasattr(o, '_latex')" is more useful # to use here, than these explicit imports. elif isinstance(o, (Basic, MatrixBase, Vector, Dyadic)): return True elif isinstance(o, (float, integer_types)) and print_builtin: return True return False def _print_latex_png(o): """ A function that returns a png rendered by an external latex distribution, falling back to matplotlib rendering """ if _can_print_latex(o): s = latex(o, mode=latex_mode) try: return _preview_wrapper(s) except RuntimeError as e: debug('preview failed with:', repr(e), ' Falling back to matplotlib backend') if latex_mode != 'inline': s = latex(o, mode='inline') return _matplotlib_wrapper(s) def _print_latex_matplotlib(o): """ A function that returns a png rendered by mathtext """ if _can_print_latex(o): s = latex(o, mode='inline') return _matplotlib_wrapper(s) def _print_latex_text(o): """ A function to generate the latex representation of sympy expressions. """ if _can_print_latex(o): s = latex(o, mode='plain') s = s.replace(r'\dag', r'\dagger') s = s.strip('$') return '$$%s$$' % s def _result_display(self, arg): """IPython's pretty-printer display hook, for use in IPython 0.10 This function was adapted from: ipython/IPython/hooks.py:155 """ if self.rc.pprint: out = stringify_func(arg) if '\n' in out: print print(out) else: print(repr(arg)) import IPython if V(IPython.__version__) >= '0.11': from sympy.core.basic import Basic from sympy.matrices.matrices import MatrixBase from sympy.physics.vector import Vector, Dyadic printable_types = [ Basic, MatrixBase, float, tuple, list, set, frozenset, dict, Vector, Dyadic ] + list(integer_types) plaintext_formatter = ip.display_formatter.formatters['text/plain'] for cls in printable_types: plaintext_formatter.for_type(cls, _print_plain) png_formatter = ip.display_formatter.formatters['image/png'] if use_latex in (True, 'png'): debug("init_printing: using png formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_png) elif use_latex == 'matplotlib': debug("init_printing: using matplotlib formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_matplotlib) else: debug("init_printing: not using any png formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #png_formatter.for_type(cls, None) if cls in png_formatter.type_printers: png_formatter.type_printers.pop(cls) latex_formatter = ip.display_formatter.formatters['text/latex'] if use_latex in (True, 'mathjax'): debug("init_printing: using mathjax formatter") for cls in printable_types: latex_formatter.for_type(cls, _print_latex_text) else: debug("init_printing: not using text/latex formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #latex_formatter.for_type(cls, None) if cls in latex_formatter.type_printers: latex_formatter.type_printers.pop(cls) else: ip.set_hook('result_display', _result_display)
def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode, print_builtin, latex_printer): """Setup printing in IPython interactive session. """ try: from IPython.lib.latextools import latex_to_png except ImportError: pass preamble = "\\documentclass[%s]{article}\n" \ "\\pagestyle{empty}\n" \ "\\usepackage{amsmath,amsfonts}%s\\begin{document}" if euler: addpackages = '\\usepackage{euler}' else: addpackages = '' preamble = preamble % (fontsize, addpackages) imagesize = 'tight' offset = "0cm,0cm" resolution = 150 dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % ( imagesize, resolution, backcolor, forecolor, offset) dvioptions = dvi.split() debug("init_printing: DVIOPTIONS:", dvioptions) debug("init_printing: PREAMBLE:", preamble) latex = latex_printer or default_latex def _print_plain(arg, p, cycle): """caller for pretty, for use in IPython 0.11""" if _can_print_latex(arg): p.text(stringify_func(arg)) else: p.text(IPython.lib.pretty.pretty(arg)) def _preview_wrapper(o): exprbuffer = BytesIO() try: preview(o, output='png', viewer='BytesIO', outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions) except Exception as e: # IPython swallows exceptions debug("png printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue() def _matplotlib_wrapper(o): # mathtext does not understand certain latex flags, so we try to # replace them with suitable subs o = o.replace(r'\operatorname', '') o = o.replace(r'\overline', r'\bar') # mathtext can't render some LaTeX commands. For example, it can't # render any LaTeX environments such as array or matrix. So here we # ensure that if mathtext fails to render, we return None. try: return latex_to_png(o) except ValueError as e: debug('matplotlib exception caught:', repr(e)) return None def _can_print_latex(o): """Return True if type o can be printed with LaTeX. If o is a container type, this is True if and only if every element of o can be printed with LaTeX. """ from sympy import Basic from sympy.matrices import MatrixBase from sympy.physics.vector import Vector, Dyadic if isinstance(o, (list, tuple, set, frozenset)): return all(_can_print_latex(i) for i in o) elif isinstance(o, dict): return all(_can_print_latex(i) and _can_print_latex(o[i]) for i in o) elif isinstance(o, bool): return False # TODO : Investigate if "elif hasattr(o, '_latex')" is more useful # to use here, than these explicit imports. elif isinstance(o, (Basic, MatrixBase, Vector, Dyadic)): return True elif isinstance(o, (float, integer_types)) and print_builtin: return True return False def _print_latex_png(o): """ A function that returns a png rendered by an external latex distribution, falling back to matplotlib rendering """ if _can_print_latex(o): s = latex(o, mode=latex_mode) try: return _preview_wrapper(s) except RuntimeError as e: debug('preview failed with:', repr(e), ' Falling back to matplotlib backend') if latex_mode != 'inline': s = latex(o, mode='inline') return _matplotlib_wrapper(s) def _print_latex_matplotlib(o): """ A function that returns a png rendered by mathtext """ if _can_print_latex(o): s = latex(o, mode='inline') return _matplotlib_wrapper(s) def _print_latex_text(o): """ A function to generate the latex representation of sympy expressions. """ if _can_print_latex(o): s = latex(o, mode='plain') s = s.replace(r'\dag', r'\dagger') s = s.strip('$') return '$$%s$$' % s def _result_display(self, arg): """IPython's pretty-printer display hook, for use in IPython 0.10 This function was adapted from: ipython/IPython/hooks.py:155 """ if self.rc.pprint: out = stringify_func(arg) if '\n' in out: print print(out) else: print(repr(arg)) import IPython if V(IPython.__version__) >= '0.11': from sympy.core.basic import Basic from sympy.matrices.matrices import MatrixBase from sympy.physics.vector import Vector, Dyadic printable_types = [Basic, MatrixBase, float, tuple, list, set, frozenset, dict, Vector, Dyadic] + list(integer_types) plaintext_formatter = ip.display_formatter.formatters['text/plain'] for cls in printable_types: plaintext_formatter.for_type(cls, _print_plain) png_formatter = ip.display_formatter.formatters['image/png'] if use_latex in (True, 'png'): debug("init_printing: using png formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_png) elif use_latex == 'matplotlib': debug("init_printing: using matplotlib formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_matplotlib) else: debug("init_printing: not using any png formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #png_formatter.for_type(cls, None) if cls in png_formatter.type_printers: png_formatter.type_printers.pop(cls) latex_formatter = ip.display_formatter.formatters['text/latex'] if use_latex in (True, 'mathjax'): debug("init_printing: using mathjax formatter") for cls in printable_types: latex_formatter.for_type(cls, _print_latex_text) else: debug("init_printing: not using text/latex formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #latex_formatter.for_type(cls, None) if cls in latex_formatter.type_printers: latex_formatter.type_printers.pop(cls) else: ip.set_hook('result_display', _result_display)
def trigsimp_groebner(expr, hints=[], quick=False, order="grlex", polynomial=False): """ Simplify trigonometric expressions using a groebner basis algorithm. This routine takes a fraction involving trigonometric or hyperbolic expressions, and tries to simplify it. The primary metric is the total degree. Some attempts are made to choose the simplest possible expression of the minimal degree, but this is non-rigorous, and also very slow (see the ``quick=True`` option). If ``polynomial`` is set to True, instead of simplifying numerator and denominator together, this function just brings numerator and denominator into a canonical form. This is much faster, but has potentially worse results. However, if the input is a polynomial, then the result is guaranteed to be an equivalent polynomial of minimal degree. The most important option is hints. Its entries can be any of the following: - a natural number - a function - an iterable of the form (func, var1, var2, ...) - anything else, interpreted as a generator A number is used to indicate that the search space should be increased. A function is used to indicate that said function is likely to occur in a simplified expression. An iterable is used indicate that func(var1 + var2 + ...) is likely to occur in a simplified . An additional generator also indicates that it is likely to occur. (See examples below). This routine carries out various computationally intensive algorithms. The option ``quick=True`` can be used to suppress one particularly slow step (at the expense of potentially more complicated results, but never at the expense of increased total degree). Examples ======== >>> from sympy.abc import x, y >>> from sympy import sin, tan, cos, sinh, cosh, tanh >>> from sympy.simplify.trigsimp import trigsimp_groebner Suppose you want to simplify ``sin(x)*cos(x)``. Naively, nothing happens: >>> ex = sin(x)*cos(x) >>> trigsimp_groebner(ex) sin(x)*cos(x) This is because ``trigsimp_groebner`` only looks for a simplification involving just ``sin(x)`` and ``cos(x)``. You can tell it to also try ``2*x`` by passing ``hints=[2]``: >>> trigsimp_groebner(ex, hints=[2]) sin(2*x)/2 >>> trigsimp_groebner(sin(x)**2 - cos(x)**2, hints=[2]) -cos(2*x) Increasing the search space this way can quickly become expensive. A much faster way is to give a specific expression that is likely to occur: >>> trigsimp_groebner(ex, hints=[sin(2*x)]) sin(2*x)/2 Hyperbolic expressions are similarly supported: >>> trigsimp_groebner(sinh(2*x)/sinh(x)) 2*cosh(x) Note how no hints had to be passed, since the expression already involved ``2*x``. The tangent function is also supported. You can either pass ``tan`` in the hints, to indicate that tan should be tried whenever cosine or sine are, or you can pass a specific generator: >>> trigsimp_groebner(sin(x)/cos(x), hints=[tan]) tan(x) >>> trigsimp_groebner(sinh(x)/cosh(x), hints=[tanh(x)]) tanh(x) Finally, you can use the iterable form to suggest that angle sum formulae should be tried: >>> ex = (tan(x) + tan(y))/(1 - tan(x)*tan(y)) >>> trigsimp_groebner(ex, hints=[(tan, x, y)]) tan(x + y) """ # TODO # - preprocess by replacing everything by funcs we can handle # - optionally use cot instead of tan # - more intelligent hinting. # For example, if the ideal is small, and we have sin(x), sin(y), # add sin(x + y) automatically... ? # - algebraic numbers ... # - expressions of lowest degree are not distinguished properly # e.g. 1 - sin(x)**2 # - we could try to order the generators intelligently, so as to influence # which monomials appear in the quotient basis # THEORY # ------ # Ratsimpmodprime above can be used to "simplify" a rational function # modulo a prime ideal. "Simplify" mainly means finding an equivalent # expression of lower total degree. # # We intend to use this to simplify trigonometric functions. To do that, # we need to decide (a) which ring to use, and (b) modulo which ideal to # simplify. In practice, (a) means settling on a list of "generators" # a, b, c, ..., such that the fraction we want to simplify is a rational # function in a, b, c, ..., with coefficients in ZZ (integers). # (2) means that we have to decide what relations to impose on the # generators. There are two practical problems: # (1) The ideal has to be *prime* (a technical term). # (2) The relations have to be polynomials in the generators. # # We typically have two kinds of generators: # - trigonometric expressions, like sin(x), cos(5*x), etc # - "everything else", like gamma(x), pi, etc. # # Since this function is trigsimp, we will concentrate on what to do with # trigonometric expressions. We can also simplify hyperbolic expressions, # but the extensions should be clear. # # One crucial point is that all *other* generators really should behave # like indeterminates. In particular if (say) "I" is one of them, then # in fact I**2 + 1 = 0 and we may and will compute non-sensical # expressions. However, we can work with a dummy and add the relation # I**2 + 1 = 0 to our ideal, then substitute back in the end. # # Now regarding trigonometric generators. We split them into groups, # according to the argument of the trigonometric functions. We want to # organise this in such a way that most trigonometric identities apply in # the same group. For example, given sin(x), cos(2*x) and cos(y), we would # group as [sin(x), cos(2*x)] and [cos(y)]. # # Our prime ideal will be built in three steps: # (1) For each group, compute a "geometrically prime" ideal of relations. # Geometrically prime means that it generates a prime ideal in # CC[gens], not just ZZ[gens]. # (2) Take the union of all the generators of the ideals for all groups. # By the geometric primality condition, this is still prime. # (3) Add further inter-group relations which preserve primality. # # Step (1) works as follows. We will isolate common factors in the # argument, so that all our generators are of the form sin(n*x), cos(n*x) # or tan(n*x), with n an integer. Suppose first there are no tan terms. # The ideal [sin(x)**2 + cos(x)**2 - 1] is geometrically prime, since # X**2 + Y**2 - 1 is irreducible over CC. # Now, if we have a generator sin(n*x), than we can, using trig identities, # express sin(n*x) as a polynomial in sin(x) and cos(x). We can add this # relation to the ideal, preserving geometric primality, since the quotient # ring is unchanged. # Thus we have treated all sin and cos terms. # For tan(n*x), we add a relation tan(n*x)*cos(n*x) - sin(n*x) = 0. # (This requires of course that we already have relations for cos(n*x) and # sin(n*x).) It is not obvious, but it seems that this preserves geometric # primality. # XXX A real proof would be nice. HELP! # Sketch that <S**2 + C**2 - 1, C*T - S> is a prime ideal of # CC[S, C, T]: # - it suffices to show that the projective closure in CP**3 is # irreducible # - using the half-angle substitutions, we can express sin(x), tan(x), # cos(x) as rational functions in tan(x/2) # - from this, we get a rational map from CP**1 to our curve # - this is a morphism, hence the curve is prime # # Step (2) is trivial. # # Step (3) works by adding selected relations of the form # sin(x + y) - sin(x)*cos(y) - sin(y)*cos(x), etc. Geometric primality is # preserved by the same argument as before. def parse_hints(hints): """Split hints into (n, funcs, iterables, gens).""" n = 1 funcs, iterables, gens = [], [], [] for e in hints: if isinstance(e, (SYMPY_INTS, Integer)): n = e elif isinstance(e, FunctionClass): funcs.append(e) elif iterable(e): iterables.append((e[0], e[1:])) # XXX sin(x+2y)? # Note: we go through polys so e.g. # sin(-x) -> -sin(x) -> sin(x) gens.extend(parallel_poly_from_expr( [e[0](x) for x in e[1:]] + [e[0](Add(*e[1:]))])[1].gens) else: gens.append(e) return n, funcs, iterables, gens def build_ideal(x, terms): """ Build generators for our ideal. Terms is an iterable with elements of the form (fn, coeff), indicating that we have a generator fn(coeff*x). If any of the terms is trigonometric, sin(x) and cos(x) are guaranteed to appear in terms. Similarly for hyperbolic functions. For tan(n*x), sin(n*x) and cos(n*x) are guaranteed. """ I = [] y = Dummy('y') for fn, coeff in terms: for c, s, t, rel in ( [cos, sin, tan, cos(x)**2 + sin(x)**2 - 1], [cosh, sinh, tanh, cosh(x)**2 - sinh(x)**2 - 1]): if coeff == 1 and fn in [c, s]: I.append(rel) elif fn == t: I.append(t(coeff*x)*c(coeff*x) - s(coeff*x)) elif fn in [c, s]: cn = fn(coeff*y).expand(trig=True).subs(y, x) I.append(fn(coeff*x) - cn) return list(set(I)) def analyse_gens(gens, hints): """ Analyse the generators ``gens``, using the hints ``hints``. The meaning of ``hints`` is described in the main docstring. Return a new list of generators, and also the ideal we should work with. """ # First parse the hints n, funcs, iterables, extragens = parse_hints(hints) debug('n=%s' % n, 'funcs:', funcs, 'iterables:', iterables, 'extragens:', extragens) # We just add the extragens to gens and analyse them as before gens = list(gens) gens.extend(extragens) # remove duplicates funcs = list(set(funcs)) iterables = list(set(iterables)) gens = list(set(gens)) # all the functions we can do anything with allfuncs = {sin, cos, tan, sinh, cosh, tanh} # sin(3*x) -> ((3, x), sin) trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens if g.func in allfuncs] # Our list of new generators - start with anything that we cannot # work with (i.e. is not a trigonometric term) freegens = [g for g in gens if g.func not in allfuncs] newgens = [] trigdict = {} for (coeff, var), fn in trigterms: trigdict.setdefault(var, []).append((coeff, fn)) res = [] # the ideal for key, val in trigdict.items(): # We have now assembeled a dictionary. Its keys are common # arguments in trigonometric expressions, and values are lists of # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we # need to deal with fn(coeff*x0). We take the rational gcd of the # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol", # all other arguments are integral multiples thereof. # We will build an ideal which works with sin(x), cos(x). # If hint tan is provided, also work with tan(x). Moreover, if # n > 1, also work with sin(k*x) for k <= n, and similarly for cos # (and tan if the hint is provided). Finally, any generators which # the ideal does not work with but we need to accommodate (either # because it was in expr or because it was provided as a hint) # we also build into the ideal. # This selection process is expressed in the list ``terms``. # build_ideal then generates the actual relations in our ideal, # from this list. fns = [x[1] for x in val] val = [x[0] for x in val] gcd = reduce(igcd, val) terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)] fs = set(funcs + fns) for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]): if any(x in fs for x in (c, s, t)): fs.add(c) fs.add(s) for fn in fs: for k in range(1, n + 1): terms.append((fn, k)) extra = [] for fn, v in terms: if fn == tan: extra.append((sin, v)) extra.append((cos, v)) if fn in [sin, cos] and tan in fs: extra.append((tan, v)) if fn == tanh: extra.append((sinh, v)) extra.append((cosh, v)) if fn in [sinh, cosh] and tanh in fs: extra.append((tanh, v)) terms.extend(extra) x = gcd*Mul(*key) r = build_ideal(x, terms) res.extend(r) newgens.extend(set(fn(v*x) for fn, v in terms)) # Add generators for compound expressions from iterables for fn, args in iterables: if fn == tan: # Tan expressions are recovered from sin and cos. iterables.extend([(sin, args), (cos, args)]) elif fn == tanh: # Tanh expressions are recovered from sihn and cosh. iterables.extend([(sinh, args), (cosh, args)]) else: dummys = symbols('d:%i' % len(args), cls=Dummy) expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args))) res.append(fn(Add(*args)) - expr) if myI in gens: res.append(myI**2 + 1) freegens.remove(myI) newgens.append(myI) return res, freegens, newgens myI = Dummy('I') expr = expr.subs(S.ImaginaryUnit, myI) subs = [(myI, S.ImaginaryUnit)] num, denom = cancel(expr).as_numer_denom() try: (pnum, pdenom), opt = parallel_poly_from_expr([num, denom]) except PolificationFailed: return expr debug('initial gens:', opt.gens) ideal, freegens, gens = analyse_gens(opt.gens, hints) debug('ideal:', ideal) debug('new gens:', gens, " -- len", len(gens)) debug('free gens:', freegens, " -- len", len(gens)) # NOTE we force the domain to be ZZ to stop polys from injecting generators # (which is usually a sign of a bug in the way we build the ideal) if not gens: return expr G = groebner(ideal, order=order, gens=gens, domain=ZZ) debug('groebner basis:', list(G), " -- len", len(G)) # If our fraction is a polynomial in the free generators, simplify all # coefficients separately: from sympy.simplify.ratsimp import ratsimpmodprime if freegens and pdenom.has_only_gens(*set(gens).intersection(pdenom.gens)): num = Poly(num, gens=gens+freegens).eject(*gens) res = [] for monom, coeff in num.terms(): ourgens = set(parallel_poly_from_expr([coeff, denom])[1].gens) # We compute the transitive closure of all generators that can # be reached from our generators through relations in the ideal. changed = True while changed: changed = False for p in ideal: p = Poly(p) if not ourgens.issuperset(p.gens) and \ not p.has_only_gens(*set(p.gens).difference(ourgens)): changed = True ourgens.update(p.exclude().gens) # NOTE preserve order! realgens = [x for x in gens if x in ourgens] # The generators of the ideal have now been (implicitly) split # into two groups: those involving ourgens and those that don't. # Since we took the transitive closure above, these two groups # live in subgrings generated by a *disjoint* set of variables. # Any sensible groebner basis algorithm will preserve this disjoint # structure (i.e. the elements of the groebner basis can be split # similarly), and and the two subsets of the groebner basis then # form groebner bases by themselves. (For the smaller generating # sets, of course.) ourG = [g.as_expr() for g in G.polys if g.has_only_gens(*ourgens.intersection(g.gens))] res.append(Mul(*[a**b for a, b in zip(freegens, monom)]) * \ ratsimpmodprime(coeff/denom, ourG, order=order, gens=realgens, quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)) return Add(*res) # NOTE The following is simpler and has less assumptions on the # groebner basis algorithm. If the above turns out to be broken, # use this. return Add(*[Mul(*[a**b for a, b in zip(freegens, monom)]) * \ ratsimpmodprime(coeff/denom, list(G), order=order, gens=gens, quick=quick, domain=ZZ) for monom, coeff in num.terms()]) else: return ratsimpmodprime( expr, list(G), order=order, gens=freegens+gens, quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)
def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode, print_builtin, latex_printer, scale, **settings): """Setup printing in IPython interactive session. """ try: from IPython.lib.latextools import latex_to_png except ImportError: pass # Guess best font color if none was given based on the ip.colors string. # From the IPython documentation: # It has four case-insensitive values: 'nocolor', 'neutral', 'linux', # 'lightbg'. The default is neutral, which should be legible on either # dark or light terminal backgrounds. linux is optimised for dark # backgrounds and lightbg for light ones. if forecolor is None: color = ip.colors.lower() if color == 'lightbg': forecolor = 'Black' elif color == 'linux': forecolor = 'White' else: # No idea, go with gray. forecolor = 'Gray' debug("init_printing: Automatic foreground color:", forecolor) preamble = "\\documentclass[varwidth,%s]{standalone}\n" \ "\\usepackage{amsmath,amsfonts}%s\\begin{document}" if euler: addpackages = '\\usepackage{euler}' else: addpackages = '' if use_latex == "svg": addpackages = addpackages + "\n\\special{color %s}" % forecolor preamble = preamble % (fontsize, addpackages) imagesize = 'tight' offset = "0cm,0cm" resolution = round(150 * scale) dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % (imagesize, resolution, backcolor, forecolor, offset) dvioptions = dvi.split() svg_scale = 150 / 72 * scale dvioptions_svg = ["--no-fonts", "--scale={}".format(svg_scale)] debug("init_printing: DVIOPTIONS:", dvioptions) debug("init_printing: DVIOPTIONS_SVG:", dvioptions_svg) debug("init_printing: PREAMBLE:", preamble) latex = latex_printer or default_latex def _print_plain(arg, p, cycle): """caller for pretty, for use in IPython 0.11""" if _can_print_latex(arg): p.text(stringify_func(arg)) else: p.text(IPython.lib.pretty.pretty(arg)) def _preview_wrapper(o): exprbuffer = BytesIO() try: preview(o, output='png', viewer='BytesIO', outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions) except Exception as e: # IPython swallows exceptions debug("png printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue() def _svg_wrapper(o): exprbuffer = BytesIO() try: preview(o, output='svg', viewer='BytesIO', outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions_svg) except Exception as e: # IPython swallows exceptions debug("svg printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue().decode('utf-8') def _matplotlib_wrapper(o): # mathtext does not understand certain latex flags, so we try to # replace them with suitable subs o = o.replace(r'\operatorname', '') o = o.replace(r'\overline', r'\bar') # mathtext can't render some LaTeX commands. For example, it can't # render any LaTeX environments such as array or matrix. So here we # ensure that if mathtext fails to render, we return None. try: try: return latex_to_png(o, color=forecolor, scale=scale) except TypeError: # Old IPython version without color and scale return latex_to_png(o) except ValueError as e: debug('matplotlib exception caught:', repr(e)) return None from sympy import Basic from sympy.matrices import MatrixBase from sympy.physics.vector import Vector, Dyadic from sympy.tensor.array import NDimArray # These should all have _repr_latex_ and _repr_latex_orig. If you update # this also update printable_types below. sympy_latex_types = (Basic, MatrixBase, Vector, Dyadic, NDimArray) def _can_print_latex(o): """Return True if type o can be printed with LaTeX. If o is a container type, this is True if and only if every element of o can be printed with LaTeX. """ try: # If you're adding another type, make sure you add it to printable_types # later in this file as well builtin_types = (list, tuple, set, frozenset) if isinstance(o, builtin_types): # If the object is a custom subclass with a custom str or # repr, use that instead. if (type(o).__str__ not in (i.__str__ for i in builtin_types) or type(o).__repr__ not in (i.__repr__ for i in builtin_types)): return False return all(_can_print_latex(i) for i in o) elif isinstance(o, dict): return all( _can_print_latex(i) and _can_print_latex(o[i]) for i in o) elif isinstance(o, bool): return False # TODO : Investigate if "elif hasattr(o, '_latex')" is more useful # to use here, than these explicit imports. elif isinstance(o, sympy_latex_types): return True elif isinstance(o, (float, integer_types)) and print_builtin: return True return False except RuntimeError: return False # This is in case maximum recursion depth is reached. # Since RecursionError is for versions of Python 3.5+ # so this is to guard against RecursionError for older versions. def _print_latex_png(o): """ A function that returns a png rendered by an external latex distribution, falling back to matplotlib rendering """ if _can_print_latex(o): s = latex(o, mode=latex_mode, **settings) if latex_mode == 'plain': s = '$\\displaystyle %s$' % s try: return _preview_wrapper(s) except RuntimeError as e: debug('preview failed with:', repr(e), ' Falling back to matplotlib backend') if latex_mode != 'inline': s = latex(o, mode='inline', **settings) return _matplotlib_wrapper(s) def _print_latex_svg(o): """ A function that returns a svg rendered by an external latex distribution, no fallback available. """ if _can_print_latex(o): s = latex(o, mode=latex_mode, **settings) if latex_mode == 'plain': s = '$\\displaystyle %s$' % s try: return _svg_wrapper(s) except RuntimeError as e: debug('preview failed with:', repr(e), ' No fallback available.') def _print_latex_matplotlib(o): """ A function that returns a png rendered by mathtext """ if _can_print_latex(o): s = latex(o, mode='inline', **settings) return _matplotlib_wrapper(s) def _print_latex_text(o): """ A function to generate the latex representation of sympy expressions. """ if _can_print_latex(o): s = latex(o, mode=latex_mode, **settings) if latex_mode == 'plain': return '$\\displaystyle %s$' % s return s def _result_display(self, arg): """IPython's pretty-printer display hook, for use in IPython 0.10 This function was adapted from: ipython/IPython/hooks.py:155 """ if self.rc.pprint: out = stringify_func(arg) if '\n' in out: print print(out) else: print(repr(arg)) import IPython if V(IPython.__version__) >= '0.11': from sympy.core.basic import Basic from sympy.matrices.matrices import MatrixBase from sympy.physics.vector import Vector, Dyadic from sympy.tensor.array import NDimArray printable_types = [ Basic, MatrixBase, float, tuple, list, set, frozenset, dict, Vector, Dyadic, NDimArray ] + list(integer_types) plaintext_formatter = ip.display_formatter.formatters['text/plain'] for cls in printable_types: plaintext_formatter.for_type(cls, _print_plain) svg_formatter = ip.display_formatter.formatters['image/svg+xml'] if use_latex in ('svg', ): debug("init_printing: using svg formatter") for cls in printable_types: svg_formatter.for_type(cls, _print_latex_svg) else: debug("init_printing: not using any svg formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #png_formatter.for_type(cls, None) if cls in svg_formatter.type_printers: svg_formatter.type_printers.pop(cls) png_formatter = ip.display_formatter.formatters['image/png'] if use_latex in (True, 'png'): debug("init_printing: using png formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_png) elif use_latex == 'matplotlib': debug("init_printing: using matplotlib formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_matplotlib) else: debug("init_printing: not using any png formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #png_formatter.for_type(cls, None) if cls in png_formatter.type_printers: png_formatter.type_printers.pop(cls) latex_formatter = ip.display_formatter.formatters['text/latex'] if use_latex in (True, 'mathjax'): debug("init_printing: using mathjax formatter") for cls in printable_types: latex_formatter.for_type(cls, _print_latex_text) for typ in sympy_latex_types: typ._repr_latex_ = typ._repr_latex_orig else: debug("init_printing: not using text/latex formatter") for cls in printable_types: # Better way to set this, but currently does not work in IPython #latex_formatter.for_type(cls, None) if cls in latex_formatter.type_printers: latex_formatter.type_printers.pop(cls) for typ in sympy_latex_types: typ._repr_latex_ = None else: ip.set_hook('result_display', _result_display)
def ratsimpmodprime(expr, G, *gens, **args): """ Simplifies a rational expression ``expr`` modulo the prime ideal generated by ``G``. ``G`` should be a Groebner basis of the ideal. >>> from sympy.simplify.ratsimp import ratsimpmodprime >>> from sympy.abc import x, y >>> eq = (x + y**5 + y)/(x - y) >>> ratsimpmodprime(eq, [x*y**5 - x - y], x, y, order='lex') (x**2 + x*y + x + y)/(x**2 - x*y) If ``polynomial`` is False, the algorithm computes a rational simplification which minimizes the sum of the total degrees of the numerator and the denominator. If ``polynomial`` is True, this function just brings numerator and denominator into a canonical form. This is much faster, but has potentially worse results. References ========== .. [1] M. Monagan, R. Pearce, Rational Simplification Modulo a Polynomial Ideal, http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.163.6984 (specifically, the second algorithm) """ from sympy import solve quick = args.pop('quick', True) polynomial = args.pop('polynomial', False) debug('ratsimpmodprime', expr) # usual preparation of polynomials: num, denom = cancel(expr).as_numer_denom() try: polys, opt = parallel_poly_from_expr([num, denom] + G, *gens, **args) except PolificationFailed: return expr domain = opt.domain if domain.has_assoc_Field: opt.domain = domain.get_field() else: raise DomainError( "can't compute rational simplification over %s" % domain) # compute only once leading_monomials = [g.LM(opt.order) for g in polys[2:]] tested = set() def staircase(n): """ Compute all monomials with degree less than ``n`` that are not divisible by any element of ``leading_monomials``. """ if n == 0: return [1] S = [] for mi in combinations_with_replacement(range(len(opt.gens)), n): m = [0]*len(opt.gens) for i in mi: m[i] += 1 if all([monomial_div(m, lmg) is None for lmg in leading_monomials]): S.append(m) return [Monomial(s).as_expr(*opt.gens) for s in S] + staircase(n - 1) def _ratsimpmodprime(a, b, allsol, N=0, D=0): r""" Computes a rational simplification of ``a/b`` which minimizes the sum of the total degrees of the numerator and the denominator. The algorithm proceeds by looking at ``a * d - b * c`` modulo the ideal generated by ``G`` for some ``c`` and ``d`` with degree less than ``a`` and ``b`` respectively. The coefficients of ``c`` and ``d`` are indeterminates and thus the coefficients of the normalform of ``a * d - b * c`` are linear polynomials in these indeterminates. If these linear polynomials, considered as system of equations, have a nontrivial solution, then `\frac{a}{b} \equiv \frac{c}{d}` modulo the ideal generated by ``G``. So, by construction, the degree of ``c`` and ``d`` is less than the degree of ``a`` and ``b``, so a simpler representation has been found. After a simpler representation has been found, the algorithm tries to reduce the degree of the numerator and denominator and returns the result afterwards. As an extension, if quick=False, we look at all possible degrees such that the total degree is less than *or equal to* the best current solution. We retain a list of all solutions of minimal degree, and try to find the best one at the end. """ c, d = a, b steps = 0 maxdeg = a.total_degree() + b.total_degree() if quick: bound = maxdeg - 1 else: bound = maxdeg while N + D <= bound: if (N, D) in tested: break tested.add((N, D)) M1 = staircase(N) M2 = staircase(D) debug('%s / %s: %s, %s' % (N, D, M1, M2)) Cs = symbols("c:%d" % len(M1), cls=Dummy) Ds = symbols("d:%d" % len(M2), cls=Dummy) ng = Cs + Ds c_hat = Poly( sum([Cs[i] * M1[i] for i in range(len(M1))]), opt.gens + ng) d_hat = Poly( sum([Ds[i] * M2[i] for i in range(len(M2))]), opt.gens + ng) r = reduced(a * d_hat - b * c_hat, G, opt.gens + ng, order=opt.order, polys=True)[1] S = Poly(r, gens=opt.gens).coeffs() sol = solve(S, Cs + Ds, particular=True, quick=True) if sol and not all([s == 0 for s in sol.values()]): c = c_hat.subs(sol) d = d_hat.subs(sol) # The "free" variables occurring before as parameters # might still be in the substituted c, d, so set them # to the value chosen before: c = c.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) d = d.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) c = Poly(c, opt.gens) d = Poly(d, opt.gens) if d == 0: raise ValueError('Ideal not prime?') allsol.append((c_hat, d_hat, S, Cs + Ds)) if N + D != maxdeg: allsol = [allsol[-1]] break steps += 1 N += 1 D += 1 if steps > 0: c, d, allsol = _ratsimpmodprime(c, d, allsol, N, D - steps) c, d, allsol = _ratsimpmodprime(c, d, allsol, N - steps, D) return c, d, allsol # preprocessing. this improves performance a bit when deg(num) # and deg(denom) are large: num = reduced(num, G, opt.gens, order=opt.order)[1] denom = reduced(denom, G, opt.gens, order=opt.order)[1] if polynomial: return (num/denom).cancel() c, d, allsol = _ratsimpmodprime( Poly(num, opt.gens, domain=opt.domain), Poly(denom, opt.gens, domain=opt.domain), []) if not quick and allsol: debug('Looking for best minimal solution. Got: %s' % len(allsol)) newsol = [] for c_hat, d_hat, S, ng in allsol: sol = solve(S, ng, particular=True, quick=False) newsol.append((c_hat.subs(sol), d_hat.subs(sol))) c, d = min(newsol, key=lambda x: len(x[0].terms()) + len(x[1].terms())) if not domain.is_Field: cn, c = c.clear_denoms(convert=True) dn, d = d.clear_denoms(convert=True) r = Rational(cn, dn) else: r = Rational(1) return (c*r.q)/(d*r.p)
def _ratsimpmodprime(a, b, allsol, N=0, D=0): r""" Computes a rational simplification of ``a/b`` which minimizes the sum of the total degrees of the numerator and the denominator. The algorithm proceeds by looking at ``a * d - b * c`` modulo the ideal generated by ``G`` for some ``c`` and ``d`` with degree less than ``a`` and ``b`` respectively. The coefficients of ``c`` and ``d`` are indeterminates and thus the coefficients of the normalform of ``a * d - b * c`` are linear polynomials in these indeterminates. If these linear polynomials, considered as system of equations, have a nontrivial solution, then `\frac{a}{b} \equiv \frac{c}{d}` modulo the ideal generated by ``G``. So, by construction, the degree of ``c`` and ``d`` is less than the degree of ``a`` and ``b``, so a simpler representation has been found. After a simpler representation has been found, the algorithm tries to reduce the degree of the numerator and denominator and returns the result afterwards. As an extension, if quick=False, we look at all possible degrees such that the total degree is less than *or equal to* the best current solution. We retain a list of all solutions of minimal degree, and try to find the best one at the end. """ c, d = a, b steps = 0 maxdeg = a.total_degree() + b.total_degree() if quick: bound = maxdeg - 1 else: bound = maxdeg while N + D <= bound: if (N, D) in tested: break tested.add((N, D)) M1 = staircase(N) M2 = staircase(D) debug('%s / %s: %s, %s' % (N, D, M1, M2)) Cs = symbols("c:%d" % len(M1), cls=Dummy) Ds = symbols("d:%d" % len(M2), cls=Dummy) ng = Cs + Ds c_hat = Poly( sum([Cs[i] * M1[i] for i in range(len(M1))]), opt.gens + ng) d_hat = Poly( sum([Ds[i] * M2[i] for i in range(len(M2))]), opt.gens + ng) r = reduced(a * d_hat - b * c_hat, G, opt.gens + ng, order=opt.order, polys=True)[1] S = Poly(r, gens=opt.gens).coeffs() sol = solve(S, Cs + Ds, particular=True, quick=True) if sol and not all([s == 0 for s in sol.values()]): c = c_hat.subs(sol) d = d_hat.subs(sol) # The "free" variables occurring before as parameters # might still be in the substituted c, d, so set them # to the value chosen before: c = c.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) d = d.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) c = Poly(c, opt.gens) d = Poly(d, opt.gens) if d == 0: raise ValueError('Ideal not prime?') allsol.append((c_hat, d_hat, S, Cs + Ds)) if N + D != maxdeg: allsol = [allsol[-1]] break steps += 1 N += 1 D += 1 if steps > 0: c, d, allsol = _ratsimpmodprime(c, d, allsol, N, D - steps) c, d, allsol = _ratsimpmodprime(c, d, allsol, N - steps, D) return c, d, allsol
def _ratsimpmodprime(a, b, allsol, N=0, D=0): r""" Computes a rational simplification of ``a/b`` which minimizes the sum of the total degrees of the numerator and the denominator. Explanation =========== The algorithm proceeds by looking at ``a * d - b * c`` modulo the ideal generated by ``G`` for some ``c`` and ``d`` with degree less than ``a`` and ``b`` respectively. The coefficients of ``c`` and ``d`` are indeterminates and thus the coefficients of the normalform of ``a * d - b * c`` are linear polynomials in these indeterminates. If these linear polynomials, considered as system of equations, have a nontrivial solution, then `\frac{a}{b} \equiv \frac{c}{d}` modulo the ideal generated by ``G``. So, by construction, the degree of ``c`` and ``d`` is less than the degree of ``a`` and ``b``, so a simpler representation has been found. After a simpler representation has been found, the algorithm tries to reduce the degree of the numerator and denominator and returns the result afterwards. As an extension, if quick=False, we look at all possible degrees such that the total degree is less than *or equal to* the best current solution. We retain a list of all solutions of minimal degree, and try to find the best one at the end. """ c, d = a, b steps = 0 maxdeg = a.total_degree() + b.total_degree() if quick: bound = maxdeg - 1 else: bound = maxdeg while N + D <= bound: if (N, D) in tested: break tested.add((N, D)) M1 = staircase(N) M2 = staircase(D) debug('%s / %s: %s, %s' % (N, D, M1, M2)) Cs = symbols("c:%d" % len(M1), cls=Dummy) Ds = symbols("d:%d" % len(M2), cls=Dummy) ng = Cs + Ds c_hat = Poly(sum([Cs[i] * M1[i] for i in range(len(M1))]), opt.gens + ng) d_hat = Poly(sum([Ds[i] * M2[i] for i in range(len(M2))]), opt.gens + ng) r = reduced(a * d_hat - b * c_hat, G, opt.gens + ng, order=opt.order, polys=True)[1] S = Poly(r, gens=opt.gens).coeffs() sol = solve(S, Cs + Ds, particular=True, quick=True) if sol and not all([s == 0 for s in sol.values()]): c = c_hat.subs(sol) d = d_hat.subs(sol) # The "free" variables occurring before as parameters # might still be in the substituted c, d, so set them # to the value chosen before: c = c.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) d = d.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) c = Poly(c, opt.gens) d = Poly(d, opt.gens) if d == 0: raise ValueError('Ideal not prime?') allsol.append((c_hat, d_hat, S, Cs + Ds)) if N + D != maxdeg: allsol = [allsol[-1]] break steps += 1 N += 1 D += 1 if steps > 0: c, d, allsol = _ratsimpmodprime(c, d, allsol, N, D - steps) c, d, allsol = _ratsimpmodprime(c, d, allsol, N - steps, D) return c, d, allsol
def init_printing(pretty_print=True, order=None, use_unicode=None, use_latex=None, wrap_line=None, num_columns=None, no_global=False, ip=None, euler=False, forecolor=None, backcolor='Transparent', fontsize='10pt', latex_mode='plain', print_builtin=True, str_printer=None, pretty_printer=None, latex_printer=None, scale=1.0, **settings): r""" Initializes pretty-printer depending on the environment. Parameters ========== pretty_print : boolean, default=True If True, use pretty_print to stringify or the provided pretty printer; if False, use sstrrepr to stringify or the provided string printer. order : string or None, default='lex' There are a few different settings for this parameter: lex (default), which is lexographic order; grlex, which is graded lexographic order; grevlex, which is reversed graded lexographic order; old, which is used for compatibility reasons and for long expressions; None, which sets it to lex. use_unicode : boolean or None, default=None If True, use unicode characters; if False, do not use unicode characters; if None, make a guess based on the environment. use_latex : string, boolean, or None, default=None If True, use default LaTeX rendering in GUI interfaces (png and mathjax); if False, do not use LaTeX rendering; if None, make a guess based on the environment; if 'png', enable latex rendering with an external latex compiler, falling back to matplotlib if external compilation fails; if 'matplotlib', enable LaTeX rendering with matplotlib; if 'mathjax', enable LaTeX text generation, for example MathJax rendering in IPython notebook or text rendering in LaTeX documents; if 'svg', enable LaTeX rendering with an external latex compiler, no fallback wrap_line : boolean If True, lines will wrap at the end; if False, they will not wrap but continue as one line. This is only relevant if ``pretty_print`` is True. num_columns : int or None, default=None If int, number of columns before wrapping is set to num_columns; if None, number of columns before wrapping is set to terminal width. This is only relevant if ``pretty_print`` is True. no_global : boolean, default=False If True, the settings become system wide; if False, use just for this console/session. ip : An interactive console This can either be an instance of IPython, or a class that derives from code.InteractiveConsole. euler : boolean, optional, default=False Loads the euler package in the LaTeX preamble for handwritten style fonts (http://www.ctan.org/pkg/euler). forecolor : string or None, optional, default=None DVI setting for foreground color. None means that either 'Black', 'White', or 'Gray' will be selected based on a guess of the IPython terminal color setting. See notes. backcolor : string, optional, default='Transparent' DVI setting for background color. See notes. fontsize : string, optional, default='10pt' A font size to pass to the LaTeX documentclass function in the preamble. Note that the options are limited by the documentclass. Consider using scale instead. latex_mode : string, optional, default='plain' The mode used in the LaTeX printer. Can be one of: {'inline'|'plain'|'equation'|'equation*'}. print_builtin : boolean, optional, default=True If ``True`` then floats and integers will be printed. If ``False`` the printer will only print SymPy types. str_printer : function, optional, default=None A custom string printer function. This should mimic sympy.printing.sstrrepr(). pretty_printer : function, optional, default=None A custom pretty printer. This should mimic sympy.printing.pretty(). latex_printer : function, optional, default=None A custom LaTeX printer. This should mimic sympy.printing.latex(). scale : float, optional, default=1.0 Scale the LaTeX output when using the ``png`` or ``svg`` backends. Useful for high dpi screens. settings : Any additional settings for the ``latex`` and ``pretty`` commands can be used to fine-tune the output. Examples ======== >>> from sympy.interactive import init_printing >>> from sympy import Symbol, sqrt >>> from sympy.abc import x, y >>> sqrt(5) sqrt(5) >>> init_printing(pretty_print=True) # doctest: +SKIP >>> sqrt(5) # doctest: +SKIP ___ \/ 5 >>> theta = Symbol('theta') # doctest: +SKIP >>> init_printing(use_unicode=True) # doctest: +SKIP >>> theta # doctest: +SKIP \u03b8 >>> init_printing(use_unicode=False) # doctest: +SKIP >>> theta # doctest: +SKIP theta >>> init_printing(order='lex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grlex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grevlex') # doctest: +SKIP >>> str(y * x**2 + x * y**2) # doctest: +SKIP x**2*y + x*y**2 >>> init_printing(order='old') # doctest: +SKIP >>> str(x**2 + y**2 + x + y) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(num_columns=10) # doctest: +SKIP >>> x**2 + x + y**2 + y # doctest: +SKIP x + y + x**2 + y**2 Notes ===== The foreground and background colors can be selected when using 'png' or 'svg' LaTeX rendering. Note that before the ``init_printing`` command is executed, the LaTeX rendering is handled by the IPython console and not SymPy. The colors can be selected among the 68 standard colors known to ``dvips``, for a list see [1]_. In addition, the background color can be set to 'Transparent' (which is the default value). When using the 'Auto' foreground color, the guess is based on the ``colors`` variable in the IPython console, see [2]_. Hence, if that variable is set correctly in your IPython console, there is a high chance that the output will be readable, although manual settings may be needed. References ========== .. [1] https://en.wikibooks.org/wiki/LaTeX/Colors#The_68_standard_colors_known_to_dvips .. [2] https://ipython.readthedocs.io/en/stable/config/details.html#terminal-colors See Also ======== sympy.printing.latex sympy.printing.pretty """ import sys from sympy.printing.printer import Printer if pretty_print: if pretty_printer is not None: stringify_func = pretty_printer else: from sympy.printing import pretty as stringify_func else: if str_printer is not None: stringify_func = str_printer else: from sympy.printing import sstrrepr as stringify_func # Even if ip is not passed, double check that not in IPython shell in_ipython = False if ip is None: try: ip = get_ipython() except NameError: pass else: in_ipython = (ip is not None) if ip and not in_ipython: in_ipython = _is_ipython(ip) if in_ipython and pretty_print: try: import IPython # IPython 1.0 deprecates the frontend module, so we import directly # from the terminal module to prevent a deprecation message from being # shown. if V(IPython.__version__) >= '1.0': from IPython.terminal.interactiveshell import TerminalInteractiveShell else: from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from code import InteractiveConsole except ImportError: pass else: # This will be True if we are in the qtconsole or notebook if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \ and 'ipython-console' not in ''.join(sys.argv): if use_unicode is None: debug("init_printing: Setting use_unicode to True") use_unicode = True if use_latex is None: debug("init_printing: Setting use_latex to True") use_latex = True if not NO_GLOBAL and not no_global: Printer.set_global_settings(order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: _stringify_func = stringify_func if pretty_print: stringify_func = lambda expr: \ _stringify_func(expr, order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: stringify_func = lambda expr: _stringify_func(expr, order=order) if in_ipython: mode_in_settings = settings.pop("mode", None) if mode_in_settings: debug("init_printing: Mode is not able to be set due to internals" "of IPython printing") _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode, print_builtin, latex_printer, scale, **settings) else: _init_python_printing(stringify_func, **settings)
def trigsimp_groebner(expr, hints=[], quick=False, order="grlex", polynomial=False): """ Simplify trigonometric expressions using a groebner basis algorithm. This routine takes a fraction involving trigonometric or hyperbolic expressions, and tries to simplify it. The primary metric is the total degree. Some attempts are made to choose the simplest possible expression of the minimal degree, but this is non-rigorous, and also very slow (see the ``quick=True`` option). If ``polynomial`` is set to True, instead of simplifying numerator and denominator together, this function just brings numerator and denominator into a canonical form. This is much faster, but has potentially worse results. However, if the input is a polynomial, then the result is guaranteed to be an equivalent polynomial of minimal degree. The most important option is hints. Its entries can be any of the following: - a natural number - a function - an iterable of the form (func, var1, var2, ...) - anything else, interpreted as a generator A number is used to indicate that the search space should be increased. A function is used to indicate that said function is likely to occur in a simplified expression. An iterable is used indicate that func(var1 + var2 + ...) is likely to occur in a simplified . An additional generator also indicates that it is likely to occur. (See examples below). This routine carries out various computationally intensive algorithms. The option ``quick=True`` can be used to suppress one particularly slow step (at the expense of potentially more complicated results, but never at the expense of increased total degree). Examples ======== >>> from sympy.abc import x, y >>> from sympy import sin, tan, cos, sinh, cosh, tanh >>> from sympy.simplify.trigsimp import trigsimp_groebner Suppose you want to simplify ``sin(x)*cos(x)``. Naively, nothing happens: >>> ex = sin(x)*cos(x) >>> trigsimp_groebner(ex) sin(x)*cos(x) This is because ``trigsimp_groebner`` only looks for a simplification involving just ``sin(x)`` and ``cos(x)``. You can tell it to also try ``2*x`` by passing ``hints=[2]``: >>> trigsimp_groebner(ex, hints=[2]) sin(2*x)/2 >>> trigsimp_groebner(sin(x)**2 - cos(x)**2, hints=[2]) -cos(2*x) Increasing the search space this way can quickly become expensive. A much faster way is to give a specific expression that is likely to occur: >>> trigsimp_groebner(ex, hints=[sin(2*x)]) sin(2*x)/2 Hyperbolic expressions are similarly supported: >>> trigsimp_groebner(sinh(2*x)/sinh(x)) 2*cosh(x) Note how no hints had to be passed, since the expression already involved ``2*x``. The tangent function is also supported. You can either pass ``tan`` in the hints, to indicate that than should be tried whenever cosine or sine are, or you can pass a specific generator: >>> trigsimp_groebner(sin(x)/cos(x), hints=[tan]) tan(x) >>> trigsimp_groebner(sinh(x)/cosh(x), hints=[tanh(x)]) tanh(x) Finally, you can use the iterable form to suggest that angle sum formulae should be tried: >>> ex = (tan(x) + tan(y))/(1 - tan(x)*tan(y)) >>> trigsimp_groebner(ex, hints=[(tan, x, y)]) tan(x + y) """ # TODO # - preprocess by replacing everything by funcs we can handle # - optionally use cot instead of tan # - more intelligent hinting. # For example, if the ideal is small, and we have sin(x), sin(y), # add sin(x + y) automatically... ? # - algebraic numbers ... # - expressions of lowest degree are not distinguished properly # e.g. 1 - sin(x)**2 # - we could try to order the generators intelligently, so as to influence # which monomials appear in the quotient basis # THEORY # ------ # Ratsimpmodprime above can be used to "simplify" a rational function # modulo a prime ideal. "Simplify" mainly means finding an equivalent # expression of lower total degree. # # We intend to use this to simplify trigonometric functions. To do that, # we need to decide (a) which ring to use, and (b) modulo which ideal to # simplify. In practice, (a) means settling on a list of "generators" # a, b, c, ..., such that the fraction we want to simplify is a rational # function in a, b, c, ..., with coefficients in ZZ (integers). # (2) means that we have to decide what relations to impose on the # generators. There are two practical problems: # (1) The ideal has to be *prime* (a technical term). # (2) The relations have to be polynomials in the generators. # # We typically have two kinds of generators: # - trigonometric expressions, like sin(x), cos(5*x), etc # - "everything else", like gamma(x), pi, etc. # # Since this function is trigsimp, we will concentrate on what to do with # trigonometric expressions. We can also simplify hyperbolic expressions, # but the extensions should be clear. # # One crucial point is that all *other* generators really should behave # like indeterminates. In particular if (say) "I" is one of them, then # in fact I**2 + 1 = 0 and we may and will compute non-sensical # expressions. However, we can work with a dummy and add the relation # I**2 + 1 = 0 to our ideal, then substitute back in the end. # # Now regarding trigonometric generators. We split them into groups, # according to the argument of the trigonometric functions. We want to # organise this in such a way that most trigonometric identities apply in # the same group. For example, given sin(x), cos(2*x) and cos(y), we would # group as [sin(x), cos(2*x)] and [cos(y)]. # # Our prime ideal will be built in three steps: # (1) For each group, compute a "geometrically prime" ideal of relations. # Geometrically prime means that it generates a prime ideal in # CC[gens], not just ZZ[gens]. # (2) Take the union of all the generators of the ideals for all groups. # By the geometric primality condition, this is still prime. # (3) Add further inter-group relations which preserve primality. # # Step (1) works as follows. We will isolate common factors in the # argument, so that all our generators are of the form sin(n*x), cos(n*x) # or tan(n*x), with n an integer. Suppose first there are no tan terms. # The ideal [sin(x)**2 + cos(x)**2 - 1] is geometrically prime, since # X**2 + Y**2 - 1 is irreducible over CC. # Now, if we have a generator sin(n*x), than we can, using trig identities, # express sin(n*x) as a polynomial in sin(x) and cos(x). We can add this # relation to the ideal, preserving geometric primality, since the quotient # ring is unchanged. # Thus we have treated all sin and cos terms. # For tan(n*x), we add a relation tan(n*x)*cos(n*x) - sin(n*x) = 0. # (This requires of course that we already have relations for cos(n*x) and # sin(n*x).) It is not obvious, but it seems that this preserves geometric # primality. # XXX A real proof would be nice. HELP! # Sketch that <S**2 + C**2 - 1, C*T - S> is a prime ideal of # CC[S, C, T]: # - it suffices to show that the projective closure in CP**3 is # irreducible # - using the half-angle substitutions, we can express sin(x), tan(x), # cos(x) as rational functions in tan(x/2) # - from this, we get a rational map from CP**1 to our curve # - this is a morphism, hence the curve is prime # # Step (2) is trivial. # # Step (3) works by adding selected relations of the form # sin(x + y) - sin(x)*cos(y) - sin(y)*cos(x), etc. Geometric primality is # preserved by the same argument as before. def parse_hints(hints): """Split hints into (n, funcs, iterables, gens).""" n = 1 funcs, iterables, gens = [], [], [] for e in hints: if isinstance(e, (int, Integer)): n = e elif isinstance(e, FunctionClass): funcs.append(e) elif iterable(e): iterables.append((e[0], e[1:])) # XXX sin(x+2y)? # Note: we go through polys so e.g. # sin(-x) -> -sin(x) -> sin(x) gens.extend(parallel_poly_from_expr( [e[0](x) for x in e[1:]] + [e[0](Add(*e[1:]))])[1].gens) else: gens.append(e) return n, funcs, iterables, gens def build_ideal(x, terms): """ Build generators for our ideal. Terms is an iterable with elements of the form (fn, coeff), indicating that we have a generator fn(coeff*x). If any of the terms is trigonometric, sin(x) and cos(x) are guaranteed to appear in terms. Similarly for hyperbolic functions. For tan(n*x), sin(n*x) and cos(n*x) are guaranteed. """ gens = [] I = [] y = Dummy('y') for fn, coeff in terms: for c, s, t, rel in ( [cos, sin, tan, cos(x)**2 + sin(x)**2 - 1], [cosh, sinh, tanh, cosh(x)**2 - sinh(x)**2 - 1]): if coeff == 1 and fn in [c, s]: I.append(rel) elif fn == t: I.append(t(coeff*x)*c(coeff*x) - s(coeff*x)) elif fn in [c, s]: cn = fn(coeff*y).expand(trig=True).subs(y, x) I.append(fn(coeff*x) - cn) return list(set(I)) def analyse_gens(gens, hints): """ Analyse the generators ``gens``, using the hints ``hints``. The meaning of ``hints`` is described in the main docstring. Return a new list of generators, and also the ideal we should work with. """ # First parse the hints n, funcs, iterables, extragens = parse_hints(hints) debug('n=%s' % n, 'funcs:', funcs, 'iterables:', iterables, 'extragens:', extragens) # We just add the extragens to gens and analyse them as before gens = list(gens) gens.extend(extragens) # remove duplicates funcs = list(set(funcs)) iterables = list(set(iterables)) gens = list(set(gens)) # all the functions we can do anything with allfuncs = {sin, cos, tan, sinh, cosh, tanh} # sin(3*x) -> ((3, x), sin) trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens if g.func in allfuncs] # Our list of new generators - start with anything that we cannot # work with (i.e. is not a trigonometric term) freegens = [g for g in gens if g.func not in allfuncs] newgens = [] trigdict = {} for (coeff, var), fn in trigterms: trigdict.setdefault(var, []).append((coeff, fn)) res = [] # the ideal for key, val in trigdict.items(): # We have now assembeled a dictionary. Its keys are common # arguments in trigonometric expressions, and values are lists of # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we # need to deal with fn(coeff*x0). We take the rational gcd of the # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol", # all other arguments are integral multiples thereof. # We will build an ideal which works with sin(x), cos(x). # If hint tan is provided, also work with tan(x). Moreover, if # n > 1, also work with sin(k*x) for k <= n, and similarly for cos # (and tan if the hint is provided). Finally, any generators which # the ideal does not work with but we need to accomodate (either # because it was in expr or because it was provided as a hint) # we also build into the ideal. # This selection process is expressed in the list ``terms``. # build_ideal then generates the actual relations in our ideal, # from this list. fns = [x[1] for x in val] val = [x[0] for x in val] gcd = reduce(igcd, val) terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)] fs = set(funcs + fns) for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]): if any(x in fs for x in (c, s, t)): fs.add(c) fs.add(s) for fn in fs: for k in range(1, n + 1): terms.append((fn, k)) extra = [] for fn, v in terms: if fn == tan: extra.append((sin, v)) extra.append((cos, v)) if fn in [sin, cos] and tan in fs: extra.append((tan, v)) if fn == tanh: extra.append((sinh, v)) extra.append((cosh, v)) if fn in [sinh, cosh] and tanh in fs: extra.append((tanh, v)) terms.extend(extra) x = gcd*Mul(*key) r = build_ideal(x, terms) res.extend(r) newgens.extend(set(fn(v*x) for fn, v in terms)) # Add generators for compound expressions from iterables for fn, args in iterables: if fn == tan: # Tan expressions are recovered from sin and cos. iterables.extend([(sin, args), (cos, args)]) elif fn == tanh: # Tanh expressions are recovered from sihn and cosh. iterables.extend([(sinh, args), (cosh, args)]) else: dummys = symbols('d:%i' % len(args), cls=Dummy) expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args))) res.append(fn(Add(*args)) - expr) if myI in gens: res.append(myI**2 + 1) freegens.remove(myI) newgens.append(myI) return res, freegens, newgens myI = Dummy('I') expr = expr.subs(S.ImaginaryUnit, myI) subs = [(myI, S.ImaginaryUnit)] num, denom = cancel(expr).as_numer_denom() try: (pnum, pdenom), opt = parallel_poly_from_expr([num, denom]) except PolificationFailed: return expr debug('initial gens:', opt.gens) ideal, freegens, gens = analyse_gens(opt.gens, hints) debug('ideal:', ideal) debug('new gens:', gens, " -- len", len(gens)) debug('free gens:', freegens, " -- len", len(gens)) # NOTE we force the domain to be ZZ to stop polys from injecting generators # (which is usually a sign of a bug in the way we build the ideal) if not gens: return expr G = groebner(ideal, order=order, gens=gens, domain=ZZ) debug('groebner basis:', list(G), " -- len", len(G)) # If our fraction is a polynomial in the free generators, simplify all # coefficients separately: from sympy.simplify.ratsimp import ratsimpmodprime if freegens and pdenom.has_only_gens(*set(gens).intersection(pdenom.gens)): num = Poly(num, gens=gens+freegens).eject(*gens) res = [] for monom, coeff in num.terms(): ourgens = set(parallel_poly_from_expr([coeff, denom])[1].gens) # We compute the transitive closure of all generators that can # be reached from our generators through relations in the ideal. changed = True while changed: changed = False for p in ideal: p = Poly(p) if not ourgens.issuperset(p.gens) and \ not p.has_only_gens(*set(p.gens).difference(ourgens)): changed = True ourgens.update(p.exclude().gens) # NOTE preserve order! realgens = [x for x in gens if x in ourgens] # The generators of the ideal have now been (implicitely) split # into two groups: those involving ourgens and those that don't. # Since we took the transitive closure above, these two groups # live in subgrings generated by a *disjoint* set of variables. # Any sensible groebner basis algorithm will preserve this disjoint # structure (i.e. the elements of the groebner basis can be split # similarly), and and the two subsets of the groebner basis then # form groebner bases by themselves. (For the smaller generating # sets, of course.) ourG = [g.as_expr() for g in G.polys if g.has_only_gens(*ourgens.intersection(g.gens))] res.append(Mul(*[a**b for a, b in zip(freegens, monom)]) * \ ratsimpmodprime(coeff/denom, ourG, order=order, gens=realgens, quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)) return Add(*res) # NOTE The following is simpler and has less assumptions on the # groebner basis algorithm. If the above turns out to be broken, # use this. return Add(*[Mul(*[a**b for a, b in zip(freegens, monom)]) * \ ratsimpmodprime(coeff/denom, list(G), order=order, gens=gens, quick=quick, domain=ZZ) for monom, coeff in num.terms()]) else: return ratsimpmodprime( expr, list(G), order=order, gens=freegens+gens, quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)
def init_printing(pretty_print=True, order=None, use_unicode=None, use_latex=None, wrap_line=None, num_columns=None, no_global=False, ip=None, euler=False, forecolor='Black', backcolor='Transparent', fontsize='10pt', latex_mode='equation*', print_builtin=True, str_printer=None, pretty_printer=None, latex_printer=None): """ Initializes pretty-printer depending on the environment. Parameters ========== pretty_print: boolean If True, use pretty_print to stringify or the provided pretty printer; if False, use sstrrepr to stringify or the provided string printer. order: string or None There are a few different settings for this parameter: lex (default), which is lexographic order; grlex, which is graded lexographic order; grevlex, which is reversed graded lexographic order; old, which is used for compatibility reasons and for long expressions; None, which sets it to lex. use_unicode: boolean or None If True, use unicode characters; if False, do not use unicode characters. use_latex: string, boolean, or None If True, use default latex rendering in GUI interfaces (png and mathjax); if False, do not use latex rendering; if 'png', enable latex rendering with an external latex compiler, falling back to matplotlib if external compilation fails; if 'matplotlib', enable latex rendering with matplotlib; if 'mathjax', enable latex text generation, for example MathJax rendering in IPython notebook or text rendering in LaTeX documents wrap_line: boolean If True, lines will wrap at the end; if False, they will not wrap but continue as one line. This is only relevant if `pretty_print` is True. num_columns: int or None If int, number of columns before wrapping is set to num_columns; if None, number of columns before wrapping is set to terminal width. This is only relevant if `pretty_print` is True. no_global: boolean If True, the settings become system wide; if False, use just for this console/session. ip: An interactive console This can either be an instance of IPython, or a class that derives from code.InteractiveConsole. euler: boolean, optional, default=False Loads the euler package in the LaTeX preamble for handwritten style fonts (http://www.ctan.org/pkg/euler). forecolor: string, optional, default='Black' DVI setting for foreground color. backcolor: string, optional, default='Transparent' DVI setting for background color. fontsize: string, optional, default='10pt' A font size to pass to the LaTeX documentclass function in the preamble. latex_mode: string, optional, default='equation*' The mode used in the LaTeX printer. Can be one of: {'inline'|'plain'|'equation'|'equation*'}. print_builtin: boolean, optional, default=True If true then floats and integers will be printed. If false the printer will only print SymPy types. str_printer: function, optional, default=None A custom string printer function. This should mimic sympy.printing.sstrrepr(). pretty_printer: function, optional, default=None A custom pretty printer. This should mimic sympy.printing.pretty(). latex_printer: function, optional, default=None A custom LaTeX printer. This should mimic sympy.printing.latex(). Examples ======== >>> from sympy.interactive import init_printing >>> from sympy import Symbol, sqrt >>> from sympy.abc import x, y >>> sqrt(5) sqrt(5) >>> init_printing(pretty_print=True) # doctest: +SKIP >>> sqrt(5) # doctest: +SKIP ___ \/ 5 >>> theta = Symbol('theta') # doctest: +SKIP >>> init_printing(use_unicode=True) # doctest: +SKIP >>> theta # doctest: +SKIP \u03b8 >>> init_printing(use_unicode=False) # doctest: +SKIP >>> theta # doctest: +SKIP theta >>> init_printing(order='lex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grlex') # doctest: +SKIP >>> str(y + x + y**2 + x**2) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(order='grevlex') # doctest: +SKIP >>> str(y * x**2 + x * y**2) # doctest: +SKIP x**2*y + x*y**2 >>> init_printing(order='old') # doctest: +SKIP >>> str(x**2 + y**2 + x + y) # doctest: +SKIP x**2 + x + y**2 + y >>> init_printing(num_columns=10) # doctest: +SKIP >>> x**2 + x + y**2 + y # doctest: +SKIP x + y + x**2 + y**2 """ import sys from sympy.printing.printer import Printer if pretty_print: if pretty_printer is not None: stringify_func = pretty_printer else: from sympy.printing import pretty as stringify_func else: if str_printer is not None: stringify_func = str_printer else: from sympy.printing import sstrrepr as stringify_func # Even if ip is not passed, double check that not in IPython shell in_ipython = False if ip is None: try: ip = get_ipython() except NameError: pass else: in_ipython = (ip is not None) if ip and not in_ipython: in_ipython = _is_ipython(ip) if in_ipython and pretty_print: try: import IPython # IPython 1.0 deprecates the frontend module, so we import directly # from the terminal module to prevent a deprecation message from being # shown. if V(IPython.__version__) >= '1.0': from IPython.terminal.interactiveshell import TerminalInteractiveShell else: from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from code import InteractiveConsole except ImportError: pass else: # This will be True if we are in the qtconsole or notebook if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \ and 'ipython-console' not in ''.join(sys.argv): if use_unicode is None: debug("init_printing: Setting use_unicode to True") use_unicode = True if use_latex is None: debug("init_printing: Setting use_latex to True") use_latex = True if not no_global: Printer.set_global_settings(order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: _stringify_func = stringify_func if pretty_print: stringify_func = lambda expr: \ _stringify_func(expr, order=order, use_unicode=use_unicode, wrap_line=wrap_line, num_columns=num_columns) else: stringify_func = lambda expr: _stringify_func(expr, order=order) if in_ipython: _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode, print_builtin, latex_printer) else: _init_python_printing(stringify_func)
def analyse_gens(gens, hints): """ Analyse the generators ``gens``, using the hints ``hints``. The meaning of ``hints`` is described in the main docstring. Return a new list of generators, and also the ideal we should work with. """ # First parse the hints n, funcs, iterables, extragens = parse_hints(hints) debug('n=%s' % n, 'funcs:', funcs, 'iterables:', iterables, 'extragens:', extragens) # We just add the extragens to gens and analyse them as before gens = list(gens) gens.extend(extragens) # remove duplicates funcs = list(set(funcs)) iterables = list(set(iterables)) gens = list(set(gens)) # all the functions we can do anything with allfuncs = {sin, cos, tan, sinh, cosh, tanh} # sin(3*x) -> ((3, x), sin) trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens if g.func in allfuncs] # Our list of new generators - start with anything that we cannot # work with (i.e. is not a trigonometric term) freegens = [g for g in gens if g.func not in allfuncs] newgens = [] trigdict = {} for (coeff, var), fn in trigterms: trigdict.setdefault(var, []).append((coeff, fn)) res = [] # the ideal for key, val in trigdict.items(): # We have now assembeled a dictionary. Its keys are common # arguments in trigonometric expressions, and values are lists of # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we # need to deal with fn(coeff*x0). We take the rational gcd of the # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol", # all other arguments are integral multiples thereof. # We will build an ideal which works with sin(x), cos(x). # If hint tan is provided, also work with tan(x). Moreover, if # n > 1, also work with sin(k*x) for k <= n, and similarly for cos # (and tan if the hint is provided). Finally, any generators which # the ideal does not work with but we need to accomodate (either # because it was in expr or because it was provided as a hint) # we also build into the ideal. # This selection process is expressed in the list ``terms``. # build_ideal then generates the actual relations in our ideal, # from this list. fns = [x[1] for x in val] val = [x[0] for x in val] gcd = reduce(igcd, val) terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)] fs = set(funcs + fns) for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]): if any(x in fs for x in (c, s, t)): fs.add(c) fs.add(s) for fn in fs: for k in range(1, n + 1): terms.append((fn, k)) extra = [] for fn, v in terms: if fn == tan: extra.append((sin, v)) extra.append((cos, v)) if fn in [sin, cos] and tan in fs: extra.append((tan, v)) if fn == tanh: extra.append((sinh, v)) extra.append((cosh, v)) if fn in [sinh, cosh] and tanh in fs: extra.append((tanh, v)) terms.extend(extra) x = gcd*Mul(*key) r = build_ideal(x, terms) res.extend(r) newgens.extend(set(fn(v*x) for fn, v in terms)) # Add generators for compound expressions from iterables for fn, args in iterables: if fn == tan: # Tan expressions are recovered from sin and cos. iterables.extend([(sin, args), (cos, args)]) elif fn == tanh: # Tanh expressions are recovered from sihn and cosh. iterables.extend([(sinh, args), (cosh, args)]) else: dummys = symbols('d:%i' % len(args), cls=Dummy) expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args))) res.append(fn(Add(*args)) - expr) if myI in gens: res.append(myI**2 + 1) freegens.remove(myI) newgens.append(myI) return res, freegens, newgens
def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor, backcolor, fontsize, latex_mode): """Setup printing in IPython interactive session. """ try: from IPython.lib.latextools import latex_to_png except ImportError: pass preamble = "\\documentclass[%s]{article}\n" \ "\\pagestyle{empty}\n" \ "\\usepackage{amsmath,amsfonts}%s\\begin{document}" if euler: addpackages = '\\usepackage{euler}' else: addpackages = '' preamble = preamble % (fontsize, addpackages) imagesize = 'tight' offset = "0cm,0cm" resolution = 150 dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % ( imagesize, resolution, backcolor, forecolor, offset) dvioptions = dvi.split() debug("init_printing: DVIOPTIONS:", dvioptions) debug("init_printing: PREAMBLE:", preamble) def _print_plain(arg, p, cycle): """caller for pretty, for use in IPython 0.11""" if _can_print_latex(arg): p.text(stringify_func(arg)) else: p.text(IPython.lib.pretty.pretty(arg)) def _preview_wrapper(o): exprbuffer = BytesIO() try: preview(o, output='png', viewer='BytesIO', outputbuffer=exprbuffer, preamble=preamble, dvioptions=dvioptions) except Exception as e: # IPython swallows exceptions debug("png printing:", "_preview_wrapper exception raised:", repr(e)) raise return exprbuffer.getvalue() def _can_print_latex(o): """Return True if type o can be printed with LaTeX. If o is a container type, this is True if and only if every element of o can be printed with LaTeX. """ import sympy if isinstance(o, (list, tuple, set, frozenset)): return all(_can_print_latex(i) for i in o) elif isinstance(o, dict): return all((isinstance(i, string_types) or _can_print_latex(i)) and _can_print_latex(o[i]) for i in o) elif isinstance(o, bool): return False elif isinstance(o, (sympy.Basic, sympy.matrices.MatrixBase, float, integer_types)): return True return False def _print_latex_png(o): """ A function that returns a png rendered by an external latex distribution """ if _can_print_latex(o): s = latex(o, mode=latex_mode) return _preview_wrapper(s) def _print_latex_matplotlib(o): """ A function that returns a png rendered by mathtext """ if _can_print_latex(o): s = latex(o, mode='inline') # mathtext does not understand centain latex flags, so we try to # replace them with suitable subs s = s.replace(r'\operatorname', '') s = s.replace(r'\overline', r'\bar') return latex_to_png(s) def _print_latex_text(o): """ A function to generate the latex representation of sympy expressions. """ if _can_print_latex(o): s = latex(o, mode='plain') s = s.replace(r'\dag', r'\dagger') s = s.strip('$') return '$$%s$$' % s def _result_display(self, arg): """IPython's pretty-printer display hook, for use in IPython 0.10 This function was adapted from: ipython/IPython/hooks.py:155 """ if self.rc.pprint: out = stringify_func(arg) if '\n' in out: print print(out) else: print(repr(arg)) import IPython if IPython.__version__ >= '0.11': from sympy.core.basic import Basic from sympy.matrices.matrices import MatrixBase printable_types = [Basic, MatrixBase, float, tuple, list, set, frozenset, dict] + list(integer_types) plaintext_formatter = ip.display_formatter.formatters['text/plain'] for cls in printable_types: plaintext_formatter.for_type(cls, _print_plain) png_formatter = ip.display_formatter.formatters['image/png'] if use_latex in (True, 'png'): debug("init_printing: using png formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_png) png_formatter.enabled = True elif use_latex == 'matplotlib': debug("init_printing: using matplotlib formatter") for cls in printable_types: png_formatter.for_type(cls, _print_latex_matplotlib) png_formatter.enabled = True else: debug("init_printing: not using any png formatter") png_formatter.enabled = False latex_formatter = ip.display_formatter.formatters['text/latex'] if use_latex in (True, 'mathjax'): debug("init_printing: using mathjax formatter") for cls in printable_types: latex_formatter.for_type(cls, _print_latex_text) latex_formatter.enabled = True else: debug("init_printing: not using mathjax formatter") latex_formatter.enabled = False else: ip.set_hook('result_display', _result_display)
def ratsimpmodprime(expr, G, *gens, quick=True, polynomial=False, **args): """ Simplifies a rational expression ``expr`` modulo the prime ideal generated by ``G``. ``G`` should be a Groebner basis of the ideal. Examples ======== >>> from sympy.simplify.ratsimp import ratsimpmodprime >>> from sympy.abc import x, y >>> eq = (x + y**5 + y)/(x - y) >>> ratsimpmodprime(eq, [x*y**5 - x - y], x, y, order='lex') (-x**2 - x*y - x - y)/(-x**2 + x*y) If ``polynomial`` is ``False``, the algorithm computes a rational simplification which minimizes the sum of the total degrees of the numerator and the denominator. If ``polynomial`` is ``True``, this function just brings numerator and denominator into a canonical form. This is much faster, but has potentially worse results. References ========== .. [1] M. Monagan, R. Pearce, Rational Simplification Modulo a Polynomial Ideal, http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.163.6984 (specifically, the second algorithm) """ from sympy import solve debug('ratsimpmodprime', expr) # usual preparation of polynomials: num, denom = cancel(expr).as_numer_denom() try: polys, opt = parallel_poly_from_expr([num, denom] + G, *gens, **args) except PolificationFailed: return expr domain = opt.domain if domain.has_assoc_Field: opt.domain = domain.get_field() else: raise DomainError("can't compute rational simplification over %s" % domain) # compute only once leading_monomials = [g.LM(opt.order) for g in polys[2:]] tested = set() def staircase(n): """ Compute all monomials with degree less than ``n`` that are not divisible by any element of ``leading_monomials``. """ if n == 0: return [1] S = [] for mi in combinations_with_replacement(range(len(opt.gens)), n): m = [0] * len(opt.gens) for i in mi: m[i] += 1 if all([monomial_div(m, lmg) is None for lmg in leading_monomials]): S.append(m) return [Monomial(s).as_expr(*opt.gens) for s in S] + staircase(n - 1) def _ratsimpmodprime(a, b, allsol, N=0, D=0): r""" Computes a rational simplification of ``a/b`` which minimizes the sum of the total degrees of the numerator and the denominator. Explanation =========== The algorithm proceeds by looking at ``a * d - b * c`` modulo the ideal generated by ``G`` for some ``c`` and ``d`` with degree less than ``a`` and ``b`` respectively. The coefficients of ``c`` and ``d`` are indeterminates and thus the coefficients of the normalform of ``a * d - b * c`` are linear polynomials in these indeterminates. If these linear polynomials, considered as system of equations, have a nontrivial solution, then `\frac{a}{b} \equiv \frac{c}{d}` modulo the ideal generated by ``G``. So, by construction, the degree of ``c`` and ``d`` is less than the degree of ``a`` and ``b``, so a simpler representation has been found. After a simpler representation has been found, the algorithm tries to reduce the degree of the numerator and denominator and returns the result afterwards. As an extension, if quick=False, we look at all possible degrees such that the total degree is less than *or equal to* the best current solution. We retain a list of all solutions of minimal degree, and try to find the best one at the end. """ c, d = a, b steps = 0 maxdeg = a.total_degree() + b.total_degree() if quick: bound = maxdeg - 1 else: bound = maxdeg while N + D <= bound: if (N, D) in tested: break tested.add((N, D)) M1 = staircase(N) M2 = staircase(D) debug('%s / %s: %s, %s' % (N, D, M1, M2)) Cs = symbols("c:%d" % len(M1), cls=Dummy) Ds = symbols("d:%d" % len(M2), cls=Dummy) ng = Cs + Ds c_hat = Poly(sum([Cs[i] * M1[i] for i in range(len(M1))]), opt.gens + ng) d_hat = Poly(sum([Ds[i] * M2[i] for i in range(len(M2))]), opt.gens + ng) r = reduced(a * d_hat - b * c_hat, G, opt.gens + ng, order=opt.order, polys=True)[1] S = Poly(r, gens=opt.gens).coeffs() sol = solve(S, Cs + Ds, particular=True, quick=True) if sol and not all([s == 0 for s in sol.values()]): c = c_hat.subs(sol) d = d_hat.subs(sol) # The "free" variables occurring before as parameters # might still be in the substituted c, d, so set them # to the value chosen before: c = c.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) d = d.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds)))))) c = Poly(c, opt.gens) d = Poly(d, opt.gens) if d == 0: raise ValueError('Ideal not prime?') allsol.append((c_hat, d_hat, S, Cs + Ds)) if N + D != maxdeg: allsol = [allsol[-1]] break steps += 1 N += 1 D += 1 if steps > 0: c, d, allsol = _ratsimpmodprime(c, d, allsol, N, D - steps) c, d, allsol = _ratsimpmodprime(c, d, allsol, N - steps, D) return c, d, allsol # preprocessing. this improves performance a bit when deg(num) # and deg(denom) are large: num = reduced(num, G, opt.gens, order=opt.order)[1] denom = reduced(denom, G, opt.gens, order=opt.order)[1] if polynomial: return (num / denom).cancel() c, d, allsol = _ratsimpmodprime(Poly(num, opt.gens, domain=opt.domain), Poly(denom, opt.gens, domain=opt.domain), []) if not quick and allsol: debug('Looking for best minimal solution. Got: %s' % len(allsol)) newsol = [] for c_hat, d_hat, S, ng in allsol: sol = solve(S, ng, particular=True, quick=False) newsol.append((c_hat.subs(sol), d_hat.subs(sol))) c, d = min(newsol, key=lambda x: len(x[0].terms()) + len(x[1].terms())) if not domain.is_Field: cn, c = c.clear_denoms(convert=True) dn, d = d.clear_denoms(convert=True) r = Rational(cn, dn) else: r = Rational(1) return (c * r.q) / (d * r.p)
def analyse_gens(gens, hints): """ Analyse the generators ``gens``, using the hints ``hints``. The meaning of ``hints`` is described in the main docstring. Return a new list of generators, and also the ideal we should work with. """ # First parse the hints n, funcs, iterables, extragens = parse_hints(hints) debug('n=%s' % n, 'funcs:', funcs, 'iterables:', iterables, 'extragens:', extragens) # We just add the extragens to gens and analyse them as before gens = list(gens) gens.extend(extragens) # remove duplicates funcs = list(set(funcs)) iterables = list(set(iterables)) gens = list(set(gens)) # all the functions we can do anything with allfuncs = {sin, cos, tan, sinh, cosh, tanh} # sin(3*x) -> ((3, x), sin) trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens if g.func in allfuncs] # Our list of new generators - start with anything that we cannot # work with (i.e. is not a trigonometric term) freegens = [g for g in gens if g.func not in allfuncs] newgens = [] trigdict = {} for (coeff, var), fn in trigterms: trigdict.setdefault(var, []).append((coeff, fn)) res = [] # the ideal for key, val in trigdict.items(): # We have now assembeled a dictionary. Its keys are common # arguments in trigonometric expressions, and values are lists of # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we # need to deal with fn(coeff*x0). We take the rational gcd of the # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol", # all other arguments are integral multiples thereof. # We will build an ideal which works with sin(x), cos(x). # If hint tan is provided, also work with tan(x). Moreover, if # n > 1, also work with sin(k*x) for k <= n, and similarly for cos # (and tan if the hint is provided). Finally, any generators which # the ideal does not work with but we need to accommodate (either # because it was in expr or because it was provided as a hint) # we also build into the ideal. # This selection process is expressed in the list ``terms``. # build_ideal then generates the actual relations in our ideal, # from this list. fns = [x[1] for x in val] val = [x[0] for x in val] gcd = reduce(igcd, val) terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)] fs = set(funcs + fns) for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]): if any(x in fs for x in (c, s, t)): fs.add(c) fs.add(s) for fn in fs: for k in range(1, n + 1): terms.append((fn, k)) extra = [] for fn, v in terms: if fn == tan: extra.append((sin, v)) extra.append((cos, v)) if fn in [sin, cos] and tan in fs: extra.append((tan, v)) if fn == tanh: extra.append((sinh, v)) extra.append((cosh, v)) if fn in [sinh, cosh] and tanh in fs: extra.append((tanh, v)) terms.extend(extra) x = gcd*Mul(*key) r = build_ideal(x, terms) res.extend(r) newgens.extend(set(fn(v*x) for fn, v in terms)) # Add generators for compound expressions from iterables for fn, args in iterables: if fn == tan: # Tan expressions are recovered from sin and cos. iterables.extend([(sin, args), (cos, args)]) elif fn == tanh: # Tanh expressions are recovered from sihn and cosh. iterables.extend([(sinh, args), (cosh, args)]) else: dummys = symbols('d:%i' % len(args), cls=Dummy) expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args))) res.append(fn(Add(*args)) - expr) if myI in gens: res.append(myI**2 + 1) freegens.remove(myI) newgens.append(myI) return res, freegens, newgens
def downvalues_rules(r, parsed): ''' Function which generates parsed rules by substituting all possible combinations of default values. ''' res = [] index = 0 for i in r: debug('parsing rule {}'.format(r.index(i) + 1)) # Parse Pattern if i[1][1][0] == 'Condition': p = i[1][1][1].copy() else: p = i[1][1].copy() optional = get_default_values(p, {}) pattern = generate_sympy_from_parsed(p.copy(), replace_Int=True) pattern, free_symbols = add_wildcards(pattern, optional=optional) free_symbols = list(set(free_symbols)) #remove common symbols # Parse Transformed Expression and Constraints if i[2][0] == 'Condition': # parse rules without constraints separately constriant = divide_constraint( i[2][2], free_symbols ) # separate And constraints into individual constraints FreeQ_vars, FreeQ_x = seperate_freeq( i[2][2].copy()) # separate FreeQ into individual constraints transformed = generate_sympy_from_parsed(i[2][1].copy(), symbols=free_symbols) else: constriant = '' FreeQ_vars, FreeQ_x = [], [] transformed = generate_sympy_from_parsed(i[2].copy(), symbols=free_symbols) FreeQ_constraint = parse_freeq(FreeQ_vars, FreeQ_x, free_symbols) pattern = sympify(pattern) pattern = rubi_printer(pattern, sympy_integers=True) pattern = setWC(pattern) transformed = sympify(transformed) index += 1 if type(transformed) == Function('With') or type( transformed) == Function( 'Module'): # define separate function when With appears transformed, With_constraints = replaceWith( transformed, free_symbols, index) parsed += ' pattern' + str( index ) + ' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + With_constraints + ')' parsed += '\n{}'.format(transformed) parsed += '\n ' + 'rule' + str( index) + ' = ReplacementRule(' + 'pattern' + rubi_printer( index, sympy_integers=True) + ', lambda ' + ', '.join( free_symbols) + ' : ' + 'With{}({})'.format( index, ', '.join(free_symbols)) + ')\n ' else: transformed = rubi_printer(transformed, sympy_integers=True) parsed += ' pattern' + str( index ) + ' = Pattern(' + pattern + '' + FreeQ_constraint + '' + constriant + ')' parsed += '\n ' + 'rule' + str( index) + ' = ReplacementRule(' + 'pattern' + rubi_printer( index, sympy_integers=True) + ', lambda ' + ', '.join( free_symbols) + ' : ' + transformed + ')\n ' parsed += 'rubi.add(rule' + str(index) + ')\n\n' parsed += ' return rubi\n' return parsed