Add files using upload-large-folder tool
Browse files- lib/python3.12/site-packages/sympy/interactive/__init__.py +8 -0
- lib/python3.12/site-packages/sympy/interactive/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/interactive/__pycache__/printing.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/interactive/__pycache__/session.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/interactive/__pycache__/traversal.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/interactive/printing.py +562 -0
- lib/python3.12/site-packages/sympy/interactive/session.py +463 -0
- lib/python3.12/site-packages/sympy/interactive/tests/__init__.py +0 -0
- lib/python3.12/site-packages/sympy/interactive/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/interactive/tests/__pycache__/test_interactive.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/interactive/tests/__pycache__/test_ipython.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/interactive/tests/test_interactive.py +10 -0
- lib/python3.12/site-packages/sympy/interactive/tests/test_ipython.py +278 -0
- lib/python3.12/site-packages/sympy/interactive/traversal.py +95 -0
- lib/python3.12/site-packages/sympy/plotting/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/__pycache__/experimental_lambdify.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/__pycache__/plot.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/__pycache__/plot_implicit.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/__pycache__/plotgrid.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/__pycache__/textplot.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/__pycache__/utils.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/__init__.py +12 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/interval_arithmetic.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/interval_membership.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/lib_interval.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py +413 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/interval_membership.py +78 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/lib_interval.py +452 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__init__.py +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/test_interval_functions.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/test_interval_membership.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/test_intervalmath.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/test_interval_functions.py +415 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/test_interval_membership.py +150 -0
- lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/test_intervalmath.py +213 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/managed_window.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_curve.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_mode.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_mode_base.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_object.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_rotation.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_surface.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/util.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/printing/codeprinter.py +888 -0
- lib/python3.12/site-packages/sympy/printing/octave.py +715 -0
- lib/python3.12/site-packages/sympy/printing/printer.py +396 -0
- lib/python3.12/site-packages/sympy/printing/rcode.py +405 -0
lib/python3.12/site-packages/sympy/interactive/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Helper module for setting up interactive SymPy sessions. """
|
| 2 |
+
|
| 3 |
+
from .printing import init_printing
|
| 4 |
+
from .session import init_session
|
| 5 |
+
from .traversal import interactive_traversal
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
__all__ = ['init_printing', 'init_session', 'interactive_traversal']
|
lib/python3.12/site-packages/sympy/interactive/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (463 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/interactive/__pycache__/printing.cpython-312.pyc
ADDED
|
Binary file (22.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/interactive/__pycache__/session.cpython-312.pyc
ADDED
|
Binary file (15.6 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/interactive/__pycache__/traversal.cpython-312.pyc
ADDED
|
Binary file (3.87 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/interactive/printing.py
ADDED
|
@@ -0,0 +1,562 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tools for setting up printing in interactive sessions. """
|
| 2 |
+
|
| 3 |
+
from sympy.external.importtools import version_tuple
|
| 4 |
+
from io import BytesIO
|
| 5 |
+
|
| 6 |
+
from sympy.printing.latex import latex as default_latex
|
| 7 |
+
from sympy.printing.preview import preview
|
| 8 |
+
from sympy.utilities.misc import debug
|
| 9 |
+
from sympy.printing.defaults import Printable
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def _init_python_printing(stringify_func, **settings):
|
| 13 |
+
"""Setup printing in Python interactive session. """
|
| 14 |
+
import sys
|
| 15 |
+
import builtins
|
| 16 |
+
|
| 17 |
+
def _displayhook(arg):
|
| 18 |
+
"""Python's pretty-printer display hook.
|
| 19 |
+
|
| 20 |
+
This function was adapted from:
|
| 21 |
+
|
| 22 |
+
https://www.python.org/dev/peps/pep-0217/
|
| 23 |
+
|
| 24 |
+
"""
|
| 25 |
+
if arg is not None:
|
| 26 |
+
builtins._ = None
|
| 27 |
+
print(stringify_func(arg, **settings))
|
| 28 |
+
builtins._ = arg
|
| 29 |
+
|
| 30 |
+
sys.displayhook = _displayhook
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def _init_ipython_printing(ip, stringify_func, use_latex, euler, forecolor,
|
| 34 |
+
backcolor, fontsize, latex_mode, print_builtin,
|
| 35 |
+
latex_printer, scale, **settings):
|
| 36 |
+
"""Setup printing in IPython interactive session. """
|
| 37 |
+
try:
|
| 38 |
+
from IPython.lib.latextools import latex_to_png
|
| 39 |
+
except ImportError:
|
| 40 |
+
pass
|
| 41 |
+
|
| 42 |
+
# Guess best font color if none was given based on the ip.colors string.
|
| 43 |
+
# From the IPython documentation:
|
| 44 |
+
# It has four case-insensitive values: 'nocolor', 'neutral', 'linux',
|
| 45 |
+
# 'lightbg'. The default is neutral, which should be legible on either
|
| 46 |
+
# dark or light terminal backgrounds. linux is optimised for dark
|
| 47 |
+
# backgrounds and lightbg for light ones.
|
| 48 |
+
if forecolor is None:
|
| 49 |
+
color = ip.colors.lower()
|
| 50 |
+
if color == 'lightbg':
|
| 51 |
+
forecolor = 'Black'
|
| 52 |
+
elif color == 'linux':
|
| 53 |
+
forecolor = 'White'
|
| 54 |
+
else:
|
| 55 |
+
# No idea, go with gray.
|
| 56 |
+
forecolor = 'Gray'
|
| 57 |
+
debug("init_printing: Automatic foreground color:", forecolor)
|
| 58 |
+
|
| 59 |
+
if use_latex == "svg":
|
| 60 |
+
extra_preamble = "\n\\special{color %s}" % forecolor
|
| 61 |
+
else:
|
| 62 |
+
extra_preamble = ""
|
| 63 |
+
|
| 64 |
+
imagesize = 'tight'
|
| 65 |
+
offset = "0cm,0cm"
|
| 66 |
+
resolution = round(150*scale)
|
| 67 |
+
dvi = r"-T %s -D %d -bg %s -fg %s -O %s" % (
|
| 68 |
+
imagesize, resolution, backcolor, forecolor, offset)
|
| 69 |
+
dvioptions = dvi.split()
|
| 70 |
+
|
| 71 |
+
svg_scale = 150/72*scale
|
| 72 |
+
dvioptions_svg = ["--no-fonts", "--scale={}".format(svg_scale)]
|
| 73 |
+
|
| 74 |
+
debug("init_printing: DVIOPTIONS:", dvioptions)
|
| 75 |
+
debug("init_printing: DVIOPTIONS_SVG:", dvioptions_svg)
|
| 76 |
+
|
| 77 |
+
latex = latex_printer or default_latex
|
| 78 |
+
|
| 79 |
+
def _print_plain(arg, p, cycle):
|
| 80 |
+
"""caller for pretty, for use in IPython 0.11"""
|
| 81 |
+
if _can_print(arg):
|
| 82 |
+
p.text(stringify_func(arg))
|
| 83 |
+
else:
|
| 84 |
+
p.text(IPython.lib.pretty.pretty(arg))
|
| 85 |
+
|
| 86 |
+
def _preview_wrapper(o):
|
| 87 |
+
exprbuffer = BytesIO()
|
| 88 |
+
try:
|
| 89 |
+
preview(o, output='png', viewer='BytesIO', euler=euler,
|
| 90 |
+
outputbuffer=exprbuffer, extra_preamble=extra_preamble,
|
| 91 |
+
dvioptions=dvioptions, fontsize=fontsize)
|
| 92 |
+
except Exception as e:
|
| 93 |
+
# IPython swallows exceptions
|
| 94 |
+
debug("png printing:", "_preview_wrapper exception raised:",
|
| 95 |
+
repr(e))
|
| 96 |
+
raise
|
| 97 |
+
return exprbuffer.getvalue()
|
| 98 |
+
|
| 99 |
+
def _svg_wrapper(o):
|
| 100 |
+
exprbuffer = BytesIO()
|
| 101 |
+
try:
|
| 102 |
+
preview(o, output='svg', viewer='BytesIO', euler=euler,
|
| 103 |
+
outputbuffer=exprbuffer, extra_preamble=extra_preamble,
|
| 104 |
+
dvioptions=dvioptions_svg, fontsize=fontsize)
|
| 105 |
+
except Exception as e:
|
| 106 |
+
# IPython swallows exceptions
|
| 107 |
+
debug("svg printing:", "_preview_wrapper exception raised:",
|
| 108 |
+
repr(e))
|
| 109 |
+
raise
|
| 110 |
+
return exprbuffer.getvalue().decode('utf-8')
|
| 111 |
+
|
| 112 |
+
def _matplotlib_wrapper(o):
|
| 113 |
+
# mathtext can't render some LaTeX commands. For example, it can't
|
| 114 |
+
# render any LaTeX environments such as array or matrix. So here we
|
| 115 |
+
# ensure that if mathtext fails to render, we return None.
|
| 116 |
+
try:
|
| 117 |
+
try:
|
| 118 |
+
return latex_to_png(o, color=forecolor, scale=scale)
|
| 119 |
+
except TypeError: # Old IPython version without color and scale
|
| 120 |
+
return latex_to_png(o)
|
| 121 |
+
except ValueError as e:
|
| 122 |
+
debug('matplotlib exception caught:', repr(e))
|
| 123 |
+
return None
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
# Hook methods for builtin SymPy printers
|
| 127 |
+
printing_hooks = ('_latex', '_sympystr', '_pretty', '_sympyrepr')
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def _can_print(o):
|
| 131 |
+
"""Return True if type o can be printed with one of the SymPy printers.
|
| 132 |
+
|
| 133 |
+
If o is a container type, this is True if and only if every element of
|
| 134 |
+
o can be printed in this way.
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
try:
|
| 138 |
+
# If you're adding another type, make sure you add it to printable_types
|
| 139 |
+
# later in this file as well
|
| 140 |
+
|
| 141 |
+
builtin_types = (list, tuple, set, frozenset)
|
| 142 |
+
if isinstance(o, builtin_types):
|
| 143 |
+
# If the object is a custom subclass with a custom str or
|
| 144 |
+
# repr, use that instead.
|
| 145 |
+
if (type(o).__str__ not in (i.__str__ for i in builtin_types) or
|
| 146 |
+
type(o).__repr__ not in (i.__repr__ for i in builtin_types)):
|
| 147 |
+
return False
|
| 148 |
+
return all(_can_print(i) for i in o)
|
| 149 |
+
elif isinstance(o, dict):
|
| 150 |
+
return all(_can_print(i) and _can_print(o[i]) for i in o)
|
| 151 |
+
elif isinstance(o, bool):
|
| 152 |
+
return False
|
| 153 |
+
elif isinstance(o, Printable):
|
| 154 |
+
# types known to SymPy
|
| 155 |
+
return True
|
| 156 |
+
elif any(hasattr(o, hook) for hook in printing_hooks):
|
| 157 |
+
# types which add support themselves
|
| 158 |
+
return True
|
| 159 |
+
elif isinstance(o, (float, int)) and print_builtin:
|
| 160 |
+
return True
|
| 161 |
+
return False
|
| 162 |
+
except RuntimeError:
|
| 163 |
+
return False
|
| 164 |
+
# This is in case maximum recursion depth is reached.
|
| 165 |
+
# Since RecursionError is for versions of Python 3.5+
|
| 166 |
+
# so this is to guard against RecursionError for older versions.
|
| 167 |
+
|
| 168 |
+
def _print_latex_png(o):
|
| 169 |
+
"""
|
| 170 |
+
A function that returns a png rendered by an external latex
|
| 171 |
+
distribution, falling back to matplotlib rendering
|
| 172 |
+
"""
|
| 173 |
+
if _can_print(o):
|
| 174 |
+
s = latex(o, mode=latex_mode, **settings)
|
| 175 |
+
if latex_mode == 'plain':
|
| 176 |
+
s = '$\\displaystyle %s$' % s
|
| 177 |
+
try:
|
| 178 |
+
return _preview_wrapper(s)
|
| 179 |
+
except RuntimeError as e:
|
| 180 |
+
debug('preview failed with:', repr(e),
|
| 181 |
+
' Falling back to matplotlib backend')
|
| 182 |
+
if latex_mode != 'inline':
|
| 183 |
+
s = latex(o, mode='inline', **settings)
|
| 184 |
+
return _matplotlib_wrapper(s)
|
| 185 |
+
|
| 186 |
+
def _print_latex_svg(o):
|
| 187 |
+
"""
|
| 188 |
+
A function that returns a svg rendered by an external latex
|
| 189 |
+
distribution, no fallback available.
|
| 190 |
+
"""
|
| 191 |
+
if _can_print(o):
|
| 192 |
+
s = latex(o, mode=latex_mode, **settings)
|
| 193 |
+
if latex_mode == 'plain':
|
| 194 |
+
s = '$\\displaystyle %s$' % s
|
| 195 |
+
try:
|
| 196 |
+
return _svg_wrapper(s)
|
| 197 |
+
except RuntimeError as e:
|
| 198 |
+
debug('preview failed with:', repr(e),
|
| 199 |
+
' No fallback available.')
|
| 200 |
+
|
| 201 |
+
def _print_latex_matplotlib(o):
|
| 202 |
+
"""
|
| 203 |
+
A function that returns a png rendered by mathtext
|
| 204 |
+
"""
|
| 205 |
+
if _can_print(o):
|
| 206 |
+
s = latex(o, mode='inline', **settings)
|
| 207 |
+
return _matplotlib_wrapper(s)
|
| 208 |
+
|
| 209 |
+
def _print_latex_text(o):
|
| 210 |
+
"""
|
| 211 |
+
A function to generate the latex representation of SymPy expressions.
|
| 212 |
+
"""
|
| 213 |
+
if _can_print(o):
|
| 214 |
+
s = latex(o, mode=latex_mode, **settings)
|
| 215 |
+
if latex_mode == 'plain':
|
| 216 |
+
return '$\\displaystyle %s$' % s
|
| 217 |
+
return s
|
| 218 |
+
|
| 219 |
+
def _result_display(self, arg):
|
| 220 |
+
"""IPython's pretty-printer display hook, for use in IPython 0.10
|
| 221 |
+
|
| 222 |
+
This function was adapted from:
|
| 223 |
+
|
| 224 |
+
ipython/IPython/hooks.py:155
|
| 225 |
+
|
| 226 |
+
"""
|
| 227 |
+
if self.rc.pprint:
|
| 228 |
+
out = stringify_func(arg)
|
| 229 |
+
|
| 230 |
+
if '\n' in out:
|
| 231 |
+
print()
|
| 232 |
+
|
| 233 |
+
print(out)
|
| 234 |
+
else:
|
| 235 |
+
print(repr(arg))
|
| 236 |
+
|
| 237 |
+
import IPython
|
| 238 |
+
if version_tuple(IPython.__version__) >= version_tuple('0.11'):
|
| 239 |
+
|
| 240 |
+
# Printable is our own type, so we handle it with methods instead of
|
| 241 |
+
# the approach required by builtin types. This allows downstream
|
| 242 |
+
# packages to override the methods in their own subclasses of Printable,
|
| 243 |
+
# which avoids the effects of gh-16002.
|
| 244 |
+
printable_types = [float, tuple, list, set, frozenset, dict, int]
|
| 245 |
+
|
| 246 |
+
plaintext_formatter = ip.display_formatter.formatters['text/plain']
|
| 247 |
+
|
| 248 |
+
# Exception to the rule above: IPython has better dispatching rules
|
| 249 |
+
# for plaintext printing (xref ipython/ipython#8938), and we can't
|
| 250 |
+
# use `_repr_pretty_` without hitting a recursion error in _print_plain.
|
| 251 |
+
for cls in printable_types + [Printable]:
|
| 252 |
+
plaintext_formatter.for_type(cls, _print_plain)
|
| 253 |
+
|
| 254 |
+
svg_formatter = ip.display_formatter.formatters['image/svg+xml']
|
| 255 |
+
if use_latex in ('svg', ):
|
| 256 |
+
debug("init_printing: using svg formatter")
|
| 257 |
+
for cls in printable_types:
|
| 258 |
+
svg_formatter.for_type(cls, _print_latex_svg)
|
| 259 |
+
Printable._repr_svg_ = _print_latex_svg
|
| 260 |
+
else:
|
| 261 |
+
debug("init_printing: not using any svg formatter")
|
| 262 |
+
for cls in printable_types:
|
| 263 |
+
# Better way to set this, but currently does not work in IPython
|
| 264 |
+
#png_formatter.for_type(cls, None)
|
| 265 |
+
if cls in svg_formatter.type_printers:
|
| 266 |
+
svg_formatter.type_printers.pop(cls)
|
| 267 |
+
Printable._repr_svg_ = Printable._repr_disabled
|
| 268 |
+
|
| 269 |
+
png_formatter = ip.display_formatter.formatters['image/png']
|
| 270 |
+
if use_latex in (True, 'png'):
|
| 271 |
+
debug("init_printing: using png formatter")
|
| 272 |
+
for cls in printable_types:
|
| 273 |
+
png_formatter.for_type(cls, _print_latex_png)
|
| 274 |
+
Printable._repr_png_ = _print_latex_png
|
| 275 |
+
elif use_latex == 'matplotlib':
|
| 276 |
+
debug("init_printing: using matplotlib formatter")
|
| 277 |
+
for cls in printable_types:
|
| 278 |
+
png_formatter.for_type(cls, _print_latex_matplotlib)
|
| 279 |
+
Printable._repr_png_ = _print_latex_matplotlib
|
| 280 |
+
else:
|
| 281 |
+
debug("init_printing: not using any png formatter")
|
| 282 |
+
for cls in printable_types:
|
| 283 |
+
# Better way to set this, but currently does not work in IPython
|
| 284 |
+
#png_formatter.for_type(cls, None)
|
| 285 |
+
if cls in png_formatter.type_printers:
|
| 286 |
+
png_formatter.type_printers.pop(cls)
|
| 287 |
+
Printable._repr_png_ = Printable._repr_disabled
|
| 288 |
+
|
| 289 |
+
latex_formatter = ip.display_formatter.formatters['text/latex']
|
| 290 |
+
if use_latex in (True, 'mathjax'):
|
| 291 |
+
debug("init_printing: using mathjax formatter")
|
| 292 |
+
for cls in printable_types:
|
| 293 |
+
latex_formatter.for_type(cls, _print_latex_text)
|
| 294 |
+
Printable._repr_latex_ = _print_latex_text
|
| 295 |
+
else:
|
| 296 |
+
debug("init_printing: not using text/latex formatter")
|
| 297 |
+
for cls in printable_types:
|
| 298 |
+
# Better way to set this, but currently does not work in IPython
|
| 299 |
+
#latex_formatter.for_type(cls, None)
|
| 300 |
+
if cls in latex_formatter.type_printers:
|
| 301 |
+
latex_formatter.type_printers.pop(cls)
|
| 302 |
+
Printable._repr_latex_ = Printable._repr_disabled
|
| 303 |
+
|
| 304 |
+
else:
|
| 305 |
+
ip.set_hook('result_display', _result_display)
|
| 306 |
+
|
| 307 |
+
def _is_ipython(shell):
|
| 308 |
+
"""Is a shell instance an IPython shell?"""
|
| 309 |
+
# shortcut, so we don't import IPython if we don't have to
|
| 310 |
+
from sys import modules
|
| 311 |
+
if 'IPython' not in modules:
|
| 312 |
+
return False
|
| 313 |
+
try:
|
| 314 |
+
from IPython.core.interactiveshell import InteractiveShell
|
| 315 |
+
except ImportError:
|
| 316 |
+
# IPython < 0.11
|
| 317 |
+
try:
|
| 318 |
+
from IPython.iplib import InteractiveShell
|
| 319 |
+
except ImportError:
|
| 320 |
+
# Reaching this points means IPython has changed in a backward-incompatible way
|
| 321 |
+
# that we don't know about. Warn?
|
| 322 |
+
return False
|
| 323 |
+
return isinstance(shell, InteractiveShell)
|
| 324 |
+
|
| 325 |
+
# Used by the doctester to override the default for no_global
|
| 326 |
+
NO_GLOBAL = False
|
| 327 |
+
|
| 328 |
+
def init_printing(pretty_print=True, order=None, use_unicode=None,
|
| 329 |
+
use_latex=None, wrap_line=None, num_columns=None,
|
| 330 |
+
no_global=False, ip=None, euler=False, forecolor=None,
|
| 331 |
+
backcolor='Transparent', fontsize='10pt',
|
| 332 |
+
latex_mode='plain', print_builtin=True,
|
| 333 |
+
str_printer=None, pretty_printer=None,
|
| 334 |
+
latex_printer=None, scale=1.0, **settings):
|
| 335 |
+
r"""
|
| 336 |
+
Initializes pretty-printer depending on the environment.
|
| 337 |
+
|
| 338 |
+
Parameters
|
| 339 |
+
==========
|
| 340 |
+
|
| 341 |
+
pretty_print : bool, default=True
|
| 342 |
+
If ``True``, use :func:`~.pretty_print` to stringify or the provided pretty
|
| 343 |
+
printer; if ``False``, use :func:`~.sstrrepr` to stringify or the provided string
|
| 344 |
+
printer.
|
| 345 |
+
order : string or None, default='lex'
|
| 346 |
+
There are a few different settings for this parameter:
|
| 347 |
+
``'lex'`` (default), which is lexographic order;
|
| 348 |
+
``'grlex'``, which is graded lexographic order;
|
| 349 |
+
``'grevlex'``, which is reversed graded lexographic order;
|
| 350 |
+
``'old'``, which is used for compatibility reasons and for long expressions;
|
| 351 |
+
``None``, which sets it to lex.
|
| 352 |
+
use_unicode : bool or None, default=None
|
| 353 |
+
If ``True``, use unicode characters;
|
| 354 |
+
if ``False``, do not use unicode characters;
|
| 355 |
+
if ``None``, make a guess based on the environment.
|
| 356 |
+
use_latex : string, bool, or None, default=None
|
| 357 |
+
If ``True``, use default LaTeX rendering in GUI interfaces (png and
|
| 358 |
+
mathjax);
|
| 359 |
+
if ``False``, do not use LaTeX rendering;
|
| 360 |
+
if ``None``, make a guess based on the environment;
|
| 361 |
+
if ``'png'``, enable LaTeX rendering with an external LaTeX compiler,
|
| 362 |
+
falling back to matplotlib if external compilation fails;
|
| 363 |
+
if ``'matplotlib'``, enable LaTeX rendering with matplotlib;
|
| 364 |
+
if ``'mathjax'``, enable LaTeX text generation, for example MathJax
|
| 365 |
+
rendering in IPython notebook or text rendering in LaTeX documents;
|
| 366 |
+
if ``'svg'``, enable LaTeX rendering with an external latex compiler,
|
| 367 |
+
no fallback
|
| 368 |
+
wrap_line : bool
|
| 369 |
+
If True, lines will wrap at the end; if False, they will not wrap
|
| 370 |
+
but continue as one line. This is only relevant if ``pretty_print`` is
|
| 371 |
+
True.
|
| 372 |
+
num_columns : int or None, default=None
|
| 373 |
+
If ``int``, number of columns before wrapping is set to num_columns; if
|
| 374 |
+
``None``, number of columns before wrapping is set to terminal width.
|
| 375 |
+
This is only relevant if ``pretty_print`` is ``True``.
|
| 376 |
+
no_global : bool, default=False
|
| 377 |
+
If ``True``, the settings become system wide;
|
| 378 |
+
if ``False``, use just for this console/session.
|
| 379 |
+
ip : An interactive console
|
| 380 |
+
This can either be an instance of IPython,
|
| 381 |
+
or a class that derives from code.InteractiveConsole.
|
| 382 |
+
euler : bool, optional, default=False
|
| 383 |
+
Loads the euler package in the LaTeX preamble for handwritten style
|
| 384 |
+
fonts (https://www.ctan.org/pkg/euler).
|
| 385 |
+
forecolor : string or None, optional, default=None
|
| 386 |
+
DVI setting for foreground color. ``None`` means that either ``'Black'``,
|
| 387 |
+
``'White'``, or ``'Gray'`` will be selected based on a guess of the IPython
|
| 388 |
+
terminal color setting. See notes.
|
| 389 |
+
backcolor : string, optional, default='Transparent'
|
| 390 |
+
DVI setting for background color. See notes.
|
| 391 |
+
fontsize : string or int, optional, default='10pt'
|
| 392 |
+
A font size to pass to the LaTeX documentclass function in the
|
| 393 |
+
preamble. Note that the options are limited by the documentclass.
|
| 394 |
+
Consider using scale instead.
|
| 395 |
+
latex_mode : string, optional, default='plain'
|
| 396 |
+
The mode used in the LaTeX printer. Can be one of:
|
| 397 |
+
``{'inline'|'plain'|'equation'|'equation*'}``.
|
| 398 |
+
print_builtin : boolean, optional, default=True
|
| 399 |
+
If ``True`` then floats and integers will be printed. If ``False`` the
|
| 400 |
+
printer will only print SymPy types.
|
| 401 |
+
str_printer : function, optional, default=None
|
| 402 |
+
A custom string printer function. This should mimic
|
| 403 |
+
:func:`~.sstrrepr()`.
|
| 404 |
+
pretty_printer : function, optional, default=None
|
| 405 |
+
A custom pretty printer. This should mimic :func:`~.pretty()`.
|
| 406 |
+
latex_printer : function, optional, default=None
|
| 407 |
+
A custom LaTeX printer. This should mimic :func:`~.latex()`.
|
| 408 |
+
scale : float, optional, default=1.0
|
| 409 |
+
Scale the LaTeX output when using the ``'png'`` or ``'svg'`` backends.
|
| 410 |
+
Useful for high dpi screens.
|
| 411 |
+
settings :
|
| 412 |
+
Any additional settings for the ``latex`` and ``pretty`` commands can
|
| 413 |
+
be used to fine-tune the output.
|
| 414 |
+
|
| 415 |
+
Examples
|
| 416 |
+
========
|
| 417 |
+
|
| 418 |
+
>>> from sympy.interactive import init_printing
|
| 419 |
+
>>> from sympy import Symbol, sqrt
|
| 420 |
+
>>> from sympy.abc import x, y
|
| 421 |
+
>>> sqrt(5)
|
| 422 |
+
sqrt(5)
|
| 423 |
+
>>> init_printing(pretty_print=True) # doctest: +SKIP
|
| 424 |
+
>>> sqrt(5) # doctest: +SKIP
|
| 425 |
+
___
|
| 426 |
+
\/ 5
|
| 427 |
+
>>> theta = Symbol('theta') # doctest: +SKIP
|
| 428 |
+
>>> init_printing(use_unicode=True) # doctest: +SKIP
|
| 429 |
+
>>> theta # doctest: +SKIP
|
| 430 |
+
\u03b8
|
| 431 |
+
>>> init_printing(use_unicode=False) # doctest: +SKIP
|
| 432 |
+
>>> theta # doctest: +SKIP
|
| 433 |
+
theta
|
| 434 |
+
>>> init_printing(order='lex') # doctest: +SKIP
|
| 435 |
+
>>> str(y + x + y**2 + x**2) # doctest: +SKIP
|
| 436 |
+
x**2 + x + y**2 + y
|
| 437 |
+
>>> init_printing(order='grlex') # doctest: +SKIP
|
| 438 |
+
>>> str(y + x + y**2 + x**2) # doctest: +SKIP
|
| 439 |
+
x**2 + x + y**2 + y
|
| 440 |
+
>>> init_printing(order='grevlex') # doctest: +SKIP
|
| 441 |
+
>>> str(y * x**2 + x * y**2) # doctest: +SKIP
|
| 442 |
+
x**2*y + x*y**2
|
| 443 |
+
>>> init_printing(order='old') # doctest: +SKIP
|
| 444 |
+
>>> str(x**2 + y**2 + x + y) # doctest: +SKIP
|
| 445 |
+
x**2 + x + y**2 + y
|
| 446 |
+
>>> init_printing(num_columns=10) # doctest: +SKIP
|
| 447 |
+
>>> x**2 + x + y**2 + y # doctest: +SKIP
|
| 448 |
+
x + y +
|
| 449 |
+
x**2 + y**2
|
| 450 |
+
|
| 451 |
+
Notes
|
| 452 |
+
=====
|
| 453 |
+
|
| 454 |
+
The foreground and background colors can be selected when using ``'png'`` or
|
| 455 |
+
``'svg'`` LaTeX rendering. Note that before the ``init_printing`` command is
|
| 456 |
+
executed, the LaTeX rendering is handled by the IPython console and not SymPy.
|
| 457 |
+
|
| 458 |
+
The colors can be selected among the 68 standard colors known to ``dvips``,
|
| 459 |
+
for a list see [1]_. In addition, the background color can be
|
| 460 |
+
set to ``'Transparent'`` (which is the default value).
|
| 461 |
+
|
| 462 |
+
When using the ``'Auto'`` foreground color, the guess is based on the
|
| 463 |
+
``colors`` variable in the IPython console, see [2]_. Hence, if
|
| 464 |
+
that variable is set correctly in your IPython console, there is a high
|
| 465 |
+
chance that the output will be readable, although manual settings may be
|
| 466 |
+
needed.
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
References
|
| 470 |
+
==========
|
| 471 |
+
|
| 472 |
+
.. [1] https://en.wikibooks.org/wiki/LaTeX/Colors#The_68_standard_colors_known_to_dvips
|
| 473 |
+
|
| 474 |
+
.. [2] https://ipython.readthedocs.io/en/stable/config/details.html#terminal-colors
|
| 475 |
+
|
| 476 |
+
See Also
|
| 477 |
+
========
|
| 478 |
+
|
| 479 |
+
sympy.printing.latex
|
| 480 |
+
sympy.printing.pretty
|
| 481 |
+
|
| 482 |
+
"""
|
| 483 |
+
import sys
|
| 484 |
+
from sympy.printing.printer import Printer
|
| 485 |
+
|
| 486 |
+
if pretty_print:
|
| 487 |
+
if pretty_printer is not None:
|
| 488 |
+
stringify_func = pretty_printer
|
| 489 |
+
else:
|
| 490 |
+
from sympy.printing import pretty as stringify_func
|
| 491 |
+
else:
|
| 492 |
+
if str_printer is not None:
|
| 493 |
+
stringify_func = str_printer
|
| 494 |
+
else:
|
| 495 |
+
from sympy.printing import sstrrepr as stringify_func
|
| 496 |
+
|
| 497 |
+
# Even if ip is not passed, double check that not in IPython shell
|
| 498 |
+
in_ipython = False
|
| 499 |
+
if ip is None:
|
| 500 |
+
try:
|
| 501 |
+
ip = get_ipython()
|
| 502 |
+
except NameError:
|
| 503 |
+
pass
|
| 504 |
+
else:
|
| 505 |
+
in_ipython = (ip is not None)
|
| 506 |
+
|
| 507 |
+
if ip and not in_ipython:
|
| 508 |
+
in_ipython = _is_ipython(ip)
|
| 509 |
+
|
| 510 |
+
if in_ipython and pretty_print:
|
| 511 |
+
try:
|
| 512 |
+
import IPython
|
| 513 |
+
# IPython 1.0 deprecates the frontend module, so we import directly
|
| 514 |
+
# from the terminal module to prevent a deprecation message from being
|
| 515 |
+
# shown.
|
| 516 |
+
if version_tuple(IPython.__version__) >= version_tuple('1.0'):
|
| 517 |
+
from IPython.terminal.interactiveshell import TerminalInteractiveShell
|
| 518 |
+
else:
|
| 519 |
+
from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
|
| 520 |
+
from code import InteractiveConsole
|
| 521 |
+
except ImportError:
|
| 522 |
+
pass
|
| 523 |
+
else:
|
| 524 |
+
# This will be True if we are in the qtconsole or notebook
|
| 525 |
+
if not isinstance(ip, (InteractiveConsole, TerminalInteractiveShell)) \
|
| 526 |
+
and 'ipython-console' not in ''.join(sys.argv):
|
| 527 |
+
if use_unicode is None:
|
| 528 |
+
debug("init_printing: Setting use_unicode to True")
|
| 529 |
+
use_unicode = True
|
| 530 |
+
if use_latex is None:
|
| 531 |
+
debug("init_printing: Setting use_latex to True")
|
| 532 |
+
use_latex = True
|
| 533 |
+
|
| 534 |
+
if not NO_GLOBAL and not no_global:
|
| 535 |
+
Printer.set_global_settings(order=order, use_unicode=use_unicode,
|
| 536 |
+
wrap_line=wrap_line, num_columns=num_columns)
|
| 537 |
+
else:
|
| 538 |
+
_stringify_func = stringify_func
|
| 539 |
+
|
| 540 |
+
if pretty_print:
|
| 541 |
+
stringify_func = lambda expr, **settings: \
|
| 542 |
+
_stringify_func(expr, order=order,
|
| 543 |
+
use_unicode=use_unicode,
|
| 544 |
+
wrap_line=wrap_line,
|
| 545 |
+
num_columns=num_columns,
|
| 546 |
+
**settings)
|
| 547 |
+
else:
|
| 548 |
+
stringify_func = \
|
| 549 |
+
lambda expr, **settings: _stringify_func(
|
| 550 |
+
expr, order=order, **settings)
|
| 551 |
+
|
| 552 |
+
if in_ipython:
|
| 553 |
+
mode_in_settings = settings.pop("mode", None)
|
| 554 |
+
if mode_in_settings:
|
| 555 |
+
debug("init_printing: Mode is not able to be set due to internals"
|
| 556 |
+
"of IPython printing")
|
| 557 |
+
_init_ipython_printing(ip, stringify_func, use_latex, euler,
|
| 558 |
+
forecolor, backcolor, fontsize, latex_mode,
|
| 559 |
+
print_builtin, latex_printer, scale,
|
| 560 |
+
**settings)
|
| 561 |
+
else:
|
| 562 |
+
_init_python_printing(stringify_func, **settings)
|
lib/python3.12/site-packages/sympy/interactive/session.py
ADDED
|
@@ -0,0 +1,463 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tools for setting up interactive sessions. """
|
| 2 |
+
|
| 3 |
+
from sympy.external.gmpy import GROUND_TYPES
|
| 4 |
+
from sympy.external.importtools import version_tuple
|
| 5 |
+
|
| 6 |
+
from sympy.interactive.printing import init_printing
|
| 7 |
+
|
| 8 |
+
from sympy.utilities.misc import ARCH
|
| 9 |
+
|
| 10 |
+
preexec_source = """\
|
| 11 |
+
from sympy import *
|
| 12 |
+
x, y, z, t = symbols('x y z t')
|
| 13 |
+
k, m, n = symbols('k m n', integer=True)
|
| 14 |
+
f, g, h = symbols('f g h', cls=Function)
|
| 15 |
+
init_printing()
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
verbose_message = """\
|
| 19 |
+
These commands were executed:
|
| 20 |
+
%(source)s
|
| 21 |
+
Documentation can be found at https://docs.sympy.org/%(version)s
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
no_ipython = """\
|
| 25 |
+
Could not locate IPython. Having IPython installed is greatly recommended.
|
| 26 |
+
See http://ipython.scipy.org for more details. If you use Debian/Ubuntu,
|
| 27 |
+
just install the 'ipython' package and start isympy again.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def _make_message(ipython=True, quiet=False, source=None):
|
| 32 |
+
"""Create a banner for an interactive session. """
|
| 33 |
+
from sympy import __version__ as sympy_version
|
| 34 |
+
from sympy import SYMPY_DEBUG
|
| 35 |
+
|
| 36 |
+
import sys
|
| 37 |
+
import os
|
| 38 |
+
|
| 39 |
+
if quiet:
|
| 40 |
+
return ""
|
| 41 |
+
|
| 42 |
+
python_version = "%d.%d.%d" % sys.version_info[:3]
|
| 43 |
+
|
| 44 |
+
if ipython:
|
| 45 |
+
shell_name = "IPython"
|
| 46 |
+
else:
|
| 47 |
+
shell_name = "Python"
|
| 48 |
+
|
| 49 |
+
info = ['ground types: %s' % GROUND_TYPES]
|
| 50 |
+
|
| 51 |
+
cache = os.getenv('SYMPY_USE_CACHE')
|
| 52 |
+
|
| 53 |
+
if cache is not None and cache.lower() == 'no':
|
| 54 |
+
info.append('cache: off')
|
| 55 |
+
|
| 56 |
+
if SYMPY_DEBUG:
|
| 57 |
+
info.append('debugging: on')
|
| 58 |
+
|
| 59 |
+
args = shell_name, sympy_version, python_version, ARCH, ', '.join(info)
|
| 60 |
+
message = "%s console for SymPy %s (Python %s-%s) (%s)\n" % args
|
| 61 |
+
|
| 62 |
+
if source is None:
|
| 63 |
+
source = preexec_source
|
| 64 |
+
|
| 65 |
+
_source = ""
|
| 66 |
+
|
| 67 |
+
for line in source.split('\n')[:-1]:
|
| 68 |
+
if not line:
|
| 69 |
+
_source += '\n'
|
| 70 |
+
else:
|
| 71 |
+
_source += '>>> ' + line + '\n'
|
| 72 |
+
|
| 73 |
+
doc_version = sympy_version
|
| 74 |
+
if 'dev' in doc_version:
|
| 75 |
+
doc_version = "dev"
|
| 76 |
+
else:
|
| 77 |
+
doc_version = "%s/" % doc_version
|
| 78 |
+
|
| 79 |
+
message += '\n' + verbose_message % {'source': _source,
|
| 80 |
+
'version': doc_version}
|
| 81 |
+
|
| 82 |
+
return message
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def int_to_Integer(s):
|
| 86 |
+
"""
|
| 87 |
+
Wrap integer literals with Integer.
|
| 88 |
+
|
| 89 |
+
This is based on the decistmt example from
|
| 90 |
+
https://docs.python.org/3/library/tokenize.html.
|
| 91 |
+
|
| 92 |
+
Only integer literals are converted. Float literals are left alone.
|
| 93 |
+
|
| 94 |
+
Examples
|
| 95 |
+
========
|
| 96 |
+
|
| 97 |
+
>>> from sympy import Integer # noqa: F401
|
| 98 |
+
>>> from sympy.interactive.session import int_to_Integer
|
| 99 |
+
>>> s = '1.2 + 1/2 - 0x12 + a1'
|
| 100 |
+
>>> int_to_Integer(s)
|
| 101 |
+
'1.2 +Integer (1 )/Integer (2 )-Integer (0x12 )+a1 '
|
| 102 |
+
>>> s = 'print (1/2)'
|
| 103 |
+
>>> int_to_Integer(s)
|
| 104 |
+
'print (Integer (1 )/Integer (2 ))'
|
| 105 |
+
>>> exec(s)
|
| 106 |
+
0.5
|
| 107 |
+
>>> exec(int_to_Integer(s))
|
| 108 |
+
1/2
|
| 109 |
+
"""
|
| 110 |
+
from tokenize import generate_tokens, untokenize, NUMBER, NAME, OP
|
| 111 |
+
from io import StringIO
|
| 112 |
+
|
| 113 |
+
def _is_int(num):
|
| 114 |
+
"""
|
| 115 |
+
Returns true if string value num (with token NUMBER) represents an integer.
|
| 116 |
+
"""
|
| 117 |
+
# XXX: Is there something in the standard library that will do this?
|
| 118 |
+
if '.' in num or 'j' in num.lower() or 'e' in num.lower():
|
| 119 |
+
return False
|
| 120 |
+
return True
|
| 121 |
+
|
| 122 |
+
result = []
|
| 123 |
+
g = generate_tokens(StringIO(s).readline) # tokenize the string
|
| 124 |
+
for toknum, tokval, _, _, _ in g:
|
| 125 |
+
if toknum == NUMBER and _is_int(tokval): # replace NUMBER tokens
|
| 126 |
+
result.extend([
|
| 127 |
+
(NAME, 'Integer'),
|
| 128 |
+
(OP, '('),
|
| 129 |
+
(NUMBER, tokval),
|
| 130 |
+
(OP, ')')
|
| 131 |
+
])
|
| 132 |
+
else:
|
| 133 |
+
result.append((toknum, tokval))
|
| 134 |
+
return untokenize(result)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def enable_automatic_int_sympification(shell):
|
| 138 |
+
"""
|
| 139 |
+
Allow IPython to automatically convert integer literals to Integer.
|
| 140 |
+
"""
|
| 141 |
+
import ast
|
| 142 |
+
old_run_cell = shell.run_cell
|
| 143 |
+
|
| 144 |
+
def my_run_cell(cell, *args, **kwargs):
|
| 145 |
+
try:
|
| 146 |
+
# Check the cell for syntax errors. This way, the syntax error
|
| 147 |
+
# will show the original input, not the transformed input. The
|
| 148 |
+
# downside here is that IPython magic like %timeit will not work
|
| 149 |
+
# with transformed input (but on the other hand, IPython magic
|
| 150 |
+
# that doesn't expect transformed input will continue to work).
|
| 151 |
+
ast.parse(cell)
|
| 152 |
+
except SyntaxError:
|
| 153 |
+
pass
|
| 154 |
+
else:
|
| 155 |
+
cell = int_to_Integer(cell)
|
| 156 |
+
return old_run_cell(cell, *args, **kwargs)
|
| 157 |
+
|
| 158 |
+
shell.run_cell = my_run_cell
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def enable_automatic_symbols(shell):
|
| 162 |
+
"""Allow IPython to automatically create symbols (``isympy -a``). """
|
| 163 |
+
# XXX: This should perhaps use tokenize, like int_to_Integer() above.
|
| 164 |
+
# This would avoid re-executing the code, which can lead to subtle
|
| 165 |
+
# issues. For example:
|
| 166 |
+
#
|
| 167 |
+
# In [1]: a = 1
|
| 168 |
+
#
|
| 169 |
+
# In [2]: for i in range(10):
|
| 170 |
+
# ...: a += 1
|
| 171 |
+
# ...:
|
| 172 |
+
#
|
| 173 |
+
# In [3]: a
|
| 174 |
+
# Out[3]: 11
|
| 175 |
+
#
|
| 176 |
+
# In [4]: a = 1
|
| 177 |
+
#
|
| 178 |
+
# In [5]: for i in range(10):
|
| 179 |
+
# ...: a += 1
|
| 180 |
+
# ...: print b
|
| 181 |
+
# ...:
|
| 182 |
+
# b
|
| 183 |
+
# b
|
| 184 |
+
# b
|
| 185 |
+
# b
|
| 186 |
+
# b
|
| 187 |
+
# b
|
| 188 |
+
# b
|
| 189 |
+
# b
|
| 190 |
+
# b
|
| 191 |
+
# b
|
| 192 |
+
#
|
| 193 |
+
# In [6]: a
|
| 194 |
+
# Out[6]: 12
|
| 195 |
+
#
|
| 196 |
+
# Note how the for loop is executed again because `b` was not defined, but `a`
|
| 197 |
+
# was already incremented once, so the result is that it is incremented
|
| 198 |
+
# multiple times.
|
| 199 |
+
|
| 200 |
+
import re
|
| 201 |
+
re_nameerror = re.compile(
|
| 202 |
+
"name '(?P<symbol>[A-Za-z_][A-Za-z0-9_]*)' is not defined")
|
| 203 |
+
|
| 204 |
+
def _handler(self, etype, value, tb, tb_offset=None):
|
| 205 |
+
"""Handle :exc:`NameError` exception and allow injection of missing symbols. """
|
| 206 |
+
if etype is NameError and tb.tb_next and not tb.tb_next.tb_next:
|
| 207 |
+
match = re_nameerror.match(str(value))
|
| 208 |
+
|
| 209 |
+
if match is not None:
|
| 210 |
+
# XXX: Make sure Symbol is in scope. Otherwise you'll get infinite recursion.
|
| 211 |
+
self.run_cell("%(symbol)s = Symbol('%(symbol)s')" %
|
| 212 |
+
{'symbol': match.group("symbol")}, store_history=False)
|
| 213 |
+
|
| 214 |
+
try:
|
| 215 |
+
code = self.user_ns['In'][-1]
|
| 216 |
+
except (KeyError, IndexError):
|
| 217 |
+
pass
|
| 218 |
+
else:
|
| 219 |
+
self.run_cell(code, store_history=False)
|
| 220 |
+
return None
|
| 221 |
+
finally:
|
| 222 |
+
self.run_cell("del %s" % match.group("symbol"),
|
| 223 |
+
store_history=False)
|
| 224 |
+
|
| 225 |
+
stb = self.InteractiveTB.structured_traceback(
|
| 226 |
+
etype, value, tb, tb_offset=tb_offset)
|
| 227 |
+
self._showtraceback(etype, value, stb)
|
| 228 |
+
|
| 229 |
+
shell.set_custom_exc((NameError,), _handler)
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
def init_ipython_session(shell=None, argv=[], auto_symbols=False, auto_int_to_Integer=False):
|
| 233 |
+
"""Construct new IPython session. """
|
| 234 |
+
import IPython
|
| 235 |
+
|
| 236 |
+
if version_tuple(IPython.__version__) >= version_tuple('0.11'):
|
| 237 |
+
if not shell:
|
| 238 |
+
# use an app to parse the command line, and init config
|
| 239 |
+
# IPython 1.0 deprecates the frontend module, so we import directly
|
| 240 |
+
# from the terminal module to prevent a deprecation message from being
|
| 241 |
+
# shown.
|
| 242 |
+
if version_tuple(IPython.__version__) >= version_tuple('1.0'):
|
| 243 |
+
from IPython.terminal import ipapp
|
| 244 |
+
else:
|
| 245 |
+
from IPython.frontend.terminal import ipapp
|
| 246 |
+
app = ipapp.TerminalIPythonApp()
|
| 247 |
+
|
| 248 |
+
# don't draw IPython banner during initialization:
|
| 249 |
+
app.display_banner = False
|
| 250 |
+
app.initialize(argv)
|
| 251 |
+
|
| 252 |
+
shell = app.shell
|
| 253 |
+
|
| 254 |
+
if auto_symbols:
|
| 255 |
+
enable_automatic_symbols(shell)
|
| 256 |
+
if auto_int_to_Integer:
|
| 257 |
+
enable_automatic_int_sympification(shell)
|
| 258 |
+
|
| 259 |
+
return shell
|
| 260 |
+
else:
|
| 261 |
+
from IPython.Shell import make_IPython
|
| 262 |
+
return make_IPython(argv)
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
def init_python_session():
|
| 266 |
+
"""Construct new Python session. """
|
| 267 |
+
from code import InteractiveConsole
|
| 268 |
+
|
| 269 |
+
class SymPyConsole(InteractiveConsole):
|
| 270 |
+
"""An interactive console with readline support. """
|
| 271 |
+
|
| 272 |
+
def __init__(self):
|
| 273 |
+
ns_locals = {}
|
| 274 |
+
InteractiveConsole.__init__(self, locals=ns_locals)
|
| 275 |
+
try:
|
| 276 |
+
import rlcompleter
|
| 277 |
+
import readline
|
| 278 |
+
except ImportError:
|
| 279 |
+
pass
|
| 280 |
+
else:
|
| 281 |
+
import os
|
| 282 |
+
import atexit
|
| 283 |
+
|
| 284 |
+
readline.set_completer(rlcompleter.Completer(ns_locals).complete)
|
| 285 |
+
readline.parse_and_bind('tab: complete')
|
| 286 |
+
|
| 287 |
+
if hasattr(readline, 'read_history_file'):
|
| 288 |
+
history = os.path.expanduser('~/.sympy-history')
|
| 289 |
+
|
| 290 |
+
try:
|
| 291 |
+
readline.read_history_file(history)
|
| 292 |
+
except OSError:
|
| 293 |
+
pass
|
| 294 |
+
|
| 295 |
+
atexit.register(readline.write_history_file, history)
|
| 296 |
+
|
| 297 |
+
return SymPyConsole()
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def init_session(ipython=None, pretty_print=True, order=None,
|
| 301 |
+
use_unicode=None, use_latex=None, quiet=False, auto_symbols=False,
|
| 302 |
+
auto_int_to_Integer=False, str_printer=None, pretty_printer=None,
|
| 303 |
+
latex_printer=None, argv=[]):
|
| 304 |
+
"""
|
| 305 |
+
Initialize an embedded IPython or Python session. The IPython session is
|
| 306 |
+
initiated with the --pylab option, without the numpy imports, so that
|
| 307 |
+
matplotlib plotting can be interactive.
|
| 308 |
+
|
| 309 |
+
Parameters
|
| 310 |
+
==========
|
| 311 |
+
|
| 312 |
+
pretty_print: boolean
|
| 313 |
+
If True, use pretty_print to stringify;
|
| 314 |
+
if False, use sstrrepr to stringify.
|
| 315 |
+
order: string or None
|
| 316 |
+
There are a few different settings for this parameter:
|
| 317 |
+
lex (default), which is lexographic order;
|
| 318 |
+
grlex, which is graded lexographic order;
|
| 319 |
+
grevlex, which is reversed graded lexographic order;
|
| 320 |
+
old, which is used for compatibility reasons and for long expressions;
|
| 321 |
+
None, which sets it to lex.
|
| 322 |
+
use_unicode: boolean or None
|
| 323 |
+
If True, use unicode characters;
|
| 324 |
+
if False, do not use unicode characters.
|
| 325 |
+
use_latex: boolean or None
|
| 326 |
+
If True, use latex rendering if IPython GUI's;
|
| 327 |
+
if False, do not use latex rendering.
|
| 328 |
+
quiet: boolean
|
| 329 |
+
If True, init_session will not print messages regarding its status;
|
| 330 |
+
if False, init_session will print messages regarding its status.
|
| 331 |
+
auto_symbols: boolean
|
| 332 |
+
If True, IPython will automatically create symbols for you.
|
| 333 |
+
If False, it will not.
|
| 334 |
+
The default is False.
|
| 335 |
+
auto_int_to_Integer: boolean
|
| 336 |
+
If True, IPython will automatically wrap int literals with Integer, so
|
| 337 |
+
that things like 1/2 give Rational(1, 2).
|
| 338 |
+
If False, it will not.
|
| 339 |
+
The default is False.
|
| 340 |
+
ipython: boolean or None
|
| 341 |
+
If True, printing will initialize for an IPython console;
|
| 342 |
+
if False, printing will initialize for a normal console;
|
| 343 |
+
The default is None, which automatically determines whether we are in
|
| 344 |
+
an ipython instance or not.
|
| 345 |
+
str_printer: function, optional, default=None
|
| 346 |
+
A custom string printer function. This should mimic
|
| 347 |
+
sympy.printing.sstrrepr().
|
| 348 |
+
pretty_printer: function, optional, default=None
|
| 349 |
+
A custom pretty printer. This should mimic sympy.printing.pretty().
|
| 350 |
+
latex_printer: function, optional, default=None
|
| 351 |
+
A custom LaTeX printer. This should mimic sympy.printing.latex()
|
| 352 |
+
This should mimic sympy.printing.latex().
|
| 353 |
+
argv: list of arguments for IPython
|
| 354 |
+
See sympy.bin.isympy for options that can be used to initialize IPython.
|
| 355 |
+
|
| 356 |
+
See Also
|
| 357 |
+
========
|
| 358 |
+
|
| 359 |
+
sympy.interactive.printing.init_printing: for examples and the rest of the parameters.
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
Examples
|
| 363 |
+
========
|
| 364 |
+
|
| 365 |
+
>>> from sympy import init_session, Symbol, sin, sqrt
|
| 366 |
+
>>> sin(x) #doctest: +SKIP
|
| 367 |
+
NameError: name 'x' is not defined
|
| 368 |
+
>>> init_session() #doctest: +SKIP
|
| 369 |
+
>>> sin(x) #doctest: +SKIP
|
| 370 |
+
sin(x)
|
| 371 |
+
>>> sqrt(5) #doctest: +SKIP
|
| 372 |
+
___
|
| 373 |
+
\\/ 5
|
| 374 |
+
>>> init_session(pretty_print=False) #doctest: +SKIP
|
| 375 |
+
>>> sqrt(5) #doctest: +SKIP
|
| 376 |
+
sqrt(5)
|
| 377 |
+
>>> y + x + y**2 + x**2 #doctest: +SKIP
|
| 378 |
+
x**2 + x + y**2 + y
|
| 379 |
+
>>> init_session(order='grlex') #doctest: +SKIP
|
| 380 |
+
>>> y + x + y**2 + x**2 #doctest: +SKIP
|
| 381 |
+
x**2 + y**2 + x + y
|
| 382 |
+
>>> init_session(order='grevlex') #doctest: +SKIP
|
| 383 |
+
>>> y * x**2 + x * y**2 #doctest: +SKIP
|
| 384 |
+
x**2*y + x*y**2
|
| 385 |
+
>>> init_session(order='old') #doctest: +SKIP
|
| 386 |
+
>>> x**2 + y**2 + x + y #doctest: +SKIP
|
| 387 |
+
x + y + x**2 + y**2
|
| 388 |
+
>>> theta = Symbol('theta') #doctest: +SKIP
|
| 389 |
+
>>> theta #doctest: +SKIP
|
| 390 |
+
theta
|
| 391 |
+
>>> init_session(use_unicode=True) #doctest: +SKIP
|
| 392 |
+
>>> theta # doctest: +SKIP
|
| 393 |
+
\u03b8
|
| 394 |
+
"""
|
| 395 |
+
import sys
|
| 396 |
+
|
| 397 |
+
in_ipython = False
|
| 398 |
+
|
| 399 |
+
if ipython is not False:
|
| 400 |
+
try:
|
| 401 |
+
import IPython
|
| 402 |
+
except ImportError:
|
| 403 |
+
if ipython is True:
|
| 404 |
+
raise RuntimeError("IPython is not available on this system")
|
| 405 |
+
ip = None
|
| 406 |
+
else:
|
| 407 |
+
try:
|
| 408 |
+
from IPython import get_ipython
|
| 409 |
+
ip = get_ipython()
|
| 410 |
+
except ImportError:
|
| 411 |
+
ip = None
|
| 412 |
+
in_ipython = bool(ip)
|
| 413 |
+
if ipython is None:
|
| 414 |
+
ipython = in_ipython
|
| 415 |
+
|
| 416 |
+
if ipython is False:
|
| 417 |
+
ip = init_python_session()
|
| 418 |
+
mainloop = ip.interact
|
| 419 |
+
else:
|
| 420 |
+
ip = init_ipython_session(ip, argv=argv, auto_symbols=auto_symbols,
|
| 421 |
+
auto_int_to_Integer=auto_int_to_Integer)
|
| 422 |
+
|
| 423 |
+
if version_tuple(IPython.__version__) >= version_tuple('0.11'):
|
| 424 |
+
# runsource is gone, use run_cell instead, which doesn't
|
| 425 |
+
# take a symbol arg. The second arg is `store_history`,
|
| 426 |
+
# and False means don't add the line to IPython's history.
|
| 427 |
+
ip.runsource = lambda src, symbol='exec': ip.run_cell(src, False)
|
| 428 |
+
|
| 429 |
+
# Enable interactive plotting using pylab.
|
| 430 |
+
try:
|
| 431 |
+
ip.enable_pylab(import_all=False)
|
| 432 |
+
except Exception:
|
| 433 |
+
# Causes an import error if matplotlib is not installed.
|
| 434 |
+
# Causes other errors (depending on the backend) if there
|
| 435 |
+
# is no display, or if there is some problem in the
|
| 436 |
+
# backend, so we have a bare "except Exception" here
|
| 437 |
+
pass
|
| 438 |
+
if not in_ipython:
|
| 439 |
+
mainloop = ip.mainloop
|
| 440 |
+
|
| 441 |
+
if auto_symbols and (not ipython or version_tuple(IPython.__version__) < version_tuple('0.11')):
|
| 442 |
+
raise RuntimeError("automatic construction of symbols is possible only in IPython 0.11 or above")
|
| 443 |
+
if auto_int_to_Integer and (not ipython or version_tuple(IPython.__version__) < version_tuple('0.11')):
|
| 444 |
+
raise RuntimeError("automatic int to Integer transformation is possible only in IPython 0.11 or above")
|
| 445 |
+
|
| 446 |
+
_preexec_source = preexec_source
|
| 447 |
+
|
| 448 |
+
ip.runsource(_preexec_source, symbol='exec')
|
| 449 |
+
init_printing(pretty_print=pretty_print, order=order,
|
| 450 |
+
use_unicode=use_unicode, use_latex=use_latex, ip=ip,
|
| 451 |
+
str_printer=str_printer, pretty_printer=pretty_printer,
|
| 452 |
+
latex_printer=latex_printer)
|
| 453 |
+
|
| 454 |
+
message = _make_message(ipython, quiet, _preexec_source)
|
| 455 |
+
|
| 456 |
+
if not in_ipython:
|
| 457 |
+
print(message)
|
| 458 |
+
mainloop()
|
| 459 |
+
sys.exit('Exiting ...')
|
| 460 |
+
else:
|
| 461 |
+
print(message)
|
| 462 |
+
import atexit
|
| 463 |
+
atexit.register(lambda: print("Exiting ...\n"))
|
lib/python3.12/site-packages/sympy/interactive/tests/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/sympy/interactive/tests/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (202 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/interactive/tests/__pycache__/test_interactive.cpython-312.pyc
ADDED
|
Binary file (889 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/interactive/tests/__pycache__/test_ipython.cpython-312.pyc
ADDED
|
Binary file (13 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/interactive/tests/test_interactive.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.interactive.session import int_to_Integer
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def test_int_to_Integer():
|
| 5 |
+
assert int_to_Integer("1 + 2.2 + 0x3 + 40") == \
|
| 6 |
+
'Integer (1 )+2.2 +Integer (0x3 )+Integer (40 )'
|
| 7 |
+
assert int_to_Integer("0b101") == 'Integer (0b101 )'
|
| 8 |
+
assert int_to_Integer("ab1 + 1 + '1 + 2'") == "ab1 +Integer (1 )+'1 + 2'"
|
| 9 |
+
assert int_to_Integer("(2 + \n3)") == '(Integer (2 )+\nInteger (3 ))'
|
| 10 |
+
assert int_to_Integer("2 + 2.0 + 2j + 2e-10") == 'Integer (2 )+2.0 +2j +2e-10 '
|
lib/python3.12/site-packages/sympy/interactive/tests/test_ipython.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests of tools for setting up interactive IPython sessions. """
|
| 2 |
+
|
| 3 |
+
from sympy.interactive.session import (init_ipython_session,
|
| 4 |
+
enable_automatic_symbols, enable_automatic_int_sympification)
|
| 5 |
+
|
| 6 |
+
from sympy.core import Symbol, Rational, Integer
|
| 7 |
+
from sympy.external import import_module
|
| 8 |
+
from sympy.testing.pytest import raises
|
| 9 |
+
|
| 10 |
+
# TODO: The code below could be made more granular with something like:
|
| 11 |
+
#
|
| 12 |
+
# @requires('IPython', version=">=0.11")
|
| 13 |
+
# def test_automatic_symbols(ipython):
|
| 14 |
+
|
| 15 |
+
ipython = import_module("IPython", min_module_version="0.11")
|
| 16 |
+
|
| 17 |
+
if not ipython:
|
| 18 |
+
#bin/test will not execute any tests now
|
| 19 |
+
disabled = True
|
| 20 |
+
|
| 21 |
+
# WARNING: These tests will modify the existing IPython environment. IPython
|
| 22 |
+
# uses a single instance for its interpreter, so there is no way to isolate
|
| 23 |
+
# the test from another IPython session. It also means that if this test is
|
| 24 |
+
# run twice in the same Python session it will fail. This isn't usually a
|
| 25 |
+
# problem because the test suite is run in a subprocess by default, but if the
|
| 26 |
+
# tests are run with subprocess=False it can pollute the current IPython
|
| 27 |
+
# session. See the discussion in issue #15149.
|
| 28 |
+
|
| 29 |
+
def test_automatic_symbols():
|
| 30 |
+
# NOTE: Because of the way the hook works, you have to use run_cell(code,
|
| 31 |
+
# True). This means that the code must have no Out, or it will be printed
|
| 32 |
+
# during the tests.
|
| 33 |
+
app = init_ipython_session()
|
| 34 |
+
app.run_cell("from sympy import *")
|
| 35 |
+
|
| 36 |
+
enable_automatic_symbols(app)
|
| 37 |
+
|
| 38 |
+
symbol = "verylongsymbolname"
|
| 39 |
+
assert symbol not in app.user_ns
|
| 40 |
+
app.run_cell("a = %s" % symbol, True)
|
| 41 |
+
assert symbol not in app.user_ns
|
| 42 |
+
app.run_cell("a = type(%s)" % symbol, True)
|
| 43 |
+
assert app.user_ns['a'] == Symbol
|
| 44 |
+
app.run_cell("%s = Symbol('%s')" % (symbol, symbol), True)
|
| 45 |
+
assert symbol in app.user_ns
|
| 46 |
+
|
| 47 |
+
# Check that built-in names aren't overridden
|
| 48 |
+
app.run_cell("a = all == __builtin__.all", True)
|
| 49 |
+
assert "all" not in app.user_ns
|
| 50 |
+
assert app.user_ns['a'] is True
|
| 51 |
+
|
| 52 |
+
# Check that SymPy names aren't overridden
|
| 53 |
+
app.run_cell("import sympy")
|
| 54 |
+
app.run_cell("a = factorial == sympy.factorial", True)
|
| 55 |
+
assert app.user_ns['a'] is True
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def test_int_to_Integer():
|
| 59 |
+
# XXX: Warning, don't test with == here. 0.5 == Rational(1, 2) is True!
|
| 60 |
+
app = init_ipython_session()
|
| 61 |
+
app.run_cell("from sympy import Integer")
|
| 62 |
+
app.run_cell("a = 1")
|
| 63 |
+
assert isinstance(app.user_ns['a'], int)
|
| 64 |
+
|
| 65 |
+
enable_automatic_int_sympification(app)
|
| 66 |
+
app.run_cell("a = 1/2")
|
| 67 |
+
assert isinstance(app.user_ns['a'], Rational)
|
| 68 |
+
app.run_cell("a = 1")
|
| 69 |
+
assert isinstance(app.user_ns['a'], Integer)
|
| 70 |
+
app.run_cell("a = int(1)")
|
| 71 |
+
assert isinstance(app.user_ns['a'], int)
|
| 72 |
+
app.run_cell("a = (1/\n2)")
|
| 73 |
+
assert app.user_ns['a'] == Rational(1, 2)
|
| 74 |
+
# TODO: How can we test that the output of a SyntaxError is the original
|
| 75 |
+
# input, not the transformed input?
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def test_ipythonprinting():
|
| 79 |
+
# Initialize and setup IPython session
|
| 80 |
+
app = init_ipython_session()
|
| 81 |
+
app.run_cell("ip = get_ipython()")
|
| 82 |
+
app.run_cell("inst = ip.instance()")
|
| 83 |
+
app.run_cell("format = inst.display_formatter.format")
|
| 84 |
+
app.run_cell("from sympy import Symbol")
|
| 85 |
+
|
| 86 |
+
# Printing without printing extension
|
| 87 |
+
app.run_cell("a = format(Symbol('pi'))")
|
| 88 |
+
app.run_cell("a2 = format(Symbol('pi')**2)")
|
| 89 |
+
# Deal with API change starting at IPython 1.0
|
| 90 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 91 |
+
assert app.user_ns['a']['text/plain'] == "pi"
|
| 92 |
+
assert app.user_ns['a2']['text/plain'] == "pi**2"
|
| 93 |
+
else:
|
| 94 |
+
assert app.user_ns['a'][0]['text/plain'] == "pi"
|
| 95 |
+
assert app.user_ns['a2'][0]['text/plain'] == "pi**2"
|
| 96 |
+
|
| 97 |
+
# Load printing extension
|
| 98 |
+
app.run_cell("from sympy import init_printing")
|
| 99 |
+
app.run_cell("init_printing()")
|
| 100 |
+
# Printing with printing extension
|
| 101 |
+
app.run_cell("a = format(Symbol('pi'))")
|
| 102 |
+
app.run_cell("a2 = format(Symbol('pi')**2)")
|
| 103 |
+
# Deal with API change starting at IPython 1.0
|
| 104 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 105 |
+
assert app.user_ns['a']['text/plain'] in ('\N{GREEK SMALL LETTER PI}', 'pi')
|
| 106 |
+
assert app.user_ns['a2']['text/plain'] in (' 2\n\N{GREEK SMALL LETTER PI} ', ' 2\npi ')
|
| 107 |
+
else:
|
| 108 |
+
assert app.user_ns['a'][0]['text/plain'] in ('\N{GREEK SMALL LETTER PI}', 'pi')
|
| 109 |
+
assert app.user_ns['a2'][0]['text/plain'] in (' 2\n\N{GREEK SMALL LETTER PI} ', ' 2\npi ')
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def test_print_builtin_option():
|
| 113 |
+
# Initialize and setup IPython session
|
| 114 |
+
app = init_ipython_session()
|
| 115 |
+
app.run_cell("ip = get_ipython()")
|
| 116 |
+
app.run_cell("inst = ip.instance()")
|
| 117 |
+
app.run_cell("format = inst.display_formatter.format")
|
| 118 |
+
app.run_cell("from sympy import Symbol")
|
| 119 |
+
app.run_cell("from sympy import init_printing")
|
| 120 |
+
|
| 121 |
+
app.run_cell("a = format({Symbol('pi'): 3.14, Symbol('n_i'): 3})")
|
| 122 |
+
# Deal with API change starting at IPython 1.0
|
| 123 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 124 |
+
text = app.user_ns['a']['text/plain']
|
| 125 |
+
raises(KeyError, lambda: app.user_ns['a']['text/latex'])
|
| 126 |
+
else:
|
| 127 |
+
text = app.user_ns['a'][0]['text/plain']
|
| 128 |
+
raises(KeyError, lambda: app.user_ns['a'][0]['text/latex'])
|
| 129 |
+
# XXX: How can we make this ignore the terminal width? This test fails if
|
| 130 |
+
# the terminal is too narrow.
|
| 131 |
+
assert text in ("{pi: 3.14, n_i: 3}",
|
| 132 |
+
'{n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3, \N{GREEK SMALL LETTER PI}: 3.14}',
|
| 133 |
+
"{n_i: 3, pi: 3.14}",
|
| 134 |
+
'{\N{GREEK SMALL LETTER PI}: 3.14, n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3}')
|
| 135 |
+
|
| 136 |
+
# If we enable the default printing, then the dictionary's should render
|
| 137 |
+
# as a LaTeX version of the whole dict: ${\pi: 3.14, n_i: 3}$
|
| 138 |
+
app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
|
| 139 |
+
app.run_cell("init_printing(use_latex=True)")
|
| 140 |
+
app.run_cell("a = format({Symbol('pi'): 3.14, Symbol('n_i'): 3})")
|
| 141 |
+
# Deal with API change starting at IPython 1.0
|
| 142 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 143 |
+
text = app.user_ns['a']['text/plain']
|
| 144 |
+
latex = app.user_ns['a']['text/latex']
|
| 145 |
+
else:
|
| 146 |
+
text = app.user_ns['a'][0]['text/plain']
|
| 147 |
+
latex = app.user_ns['a'][0]['text/latex']
|
| 148 |
+
assert text in ("{pi: 3.14, n_i: 3}",
|
| 149 |
+
'{n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3, \N{GREEK SMALL LETTER PI}: 3.14}',
|
| 150 |
+
"{n_i: 3, pi: 3.14}",
|
| 151 |
+
'{\N{GREEK SMALL LETTER PI}: 3.14, n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3}')
|
| 152 |
+
assert latex == r'$\displaystyle \left\{ n_{i} : 3, \ \pi : 3.14\right\}$'
|
| 153 |
+
|
| 154 |
+
# Objects with an _latex overload should also be handled by our tuple
|
| 155 |
+
# printer.
|
| 156 |
+
app.run_cell("""\
|
| 157 |
+
class WithOverload:
|
| 158 |
+
def _latex(self, printer):
|
| 159 |
+
return r"\\LaTeX"
|
| 160 |
+
""")
|
| 161 |
+
app.run_cell("a = format((WithOverload(),))")
|
| 162 |
+
# Deal with API change starting at IPython 1.0
|
| 163 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 164 |
+
latex = app.user_ns['a']['text/latex']
|
| 165 |
+
else:
|
| 166 |
+
latex = app.user_ns['a'][0]['text/latex']
|
| 167 |
+
assert latex == r'$\displaystyle \left( \LaTeX,\right)$'
|
| 168 |
+
|
| 169 |
+
app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
|
| 170 |
+
app.run_cell("init_printing(use_latex=True, print_builtin=False)")
|
| 171 |
+
app.run_cell("a = format({Symbol('pi'): 3.14, Symbol('n_i'): 3})")
|
| 172 |
+
# Deal with API change starting at IPython 1.0
|
| 173 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 174 |
+
text = app.user_ns['a']['text/plain']
|
| 175 |
+
raises(KeyError, lambda: app.user_ns['a']['text/latex'])
|
| 176 |
+
else:
|
| 177 |
+
text = app.user_ns['a'][0]['text/plain']
|
| 178 |
+
raises(KeyError, lambda: app.user_ns['a'][0]['text/latex'])
|
| 179 |
+
# Note : In Python 3 we have one text type: str which holds Unicode data
|
| 180 |
+
# and two byte types bytes and bytearray.
|
| 181 |
+
# Python 3.3.3 + IPython 0.13.2 gives: '{n_i: 3, pi: 3.14}'
|
| 182 |
+
# Python 3.3.3 + IPython 1.1.0 gives: '{n_i: 3, pi: 3.14}'
|
| 183 |
+
assert text in ("{pi: 3.14, n_i: 3}", "{n_i: 3, pi: 3.14}")
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def test_builtin_containers():
|
| 187 |
+
# Initialize and setup IPython session
|
| 188 |
+
app = init_ipython_session()
|
| 189 |
+
app.run_cell("ip = get_ipython()")
|
| 190 |
+
app.run_cell("inst = ip.instance()")
|
| 191 |
+
app.run_cell("format = inst.display_formatter.format")
|
| 192 |
+
app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
|
| 193 |
+
app.run_cell("from sympy import init_printing, Matrix")
|
| 194 |
+
app.run_cell('init_printing(use_latex=True, use_unicode=False)')
|
| 195 |
+
|
| 196 |
+
# Make sure containers that shouldn't pretty print don't.
|
| 197 |
+
app.run_cell('a = format((True, False))')
|
| 198 |
+
app.run_cell('import sys')
|
| 199 |
+
app.run_cell('b = format(sys.flags)')
|
| 200 |
+
app.run_cell('c = format((Matrix([1, 2]),))')
|
| 201 |
+
# Deal with API change starting at IPython 1.0
|
| 202 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 203 |
+
assert app.user_ns['a']['text/plain'] == '(True, False)'
|
| 204 |
+
assert 'text/latex' not in app.user_ns['a']
|
| 205 |
+
assert app.user_ns['b']['text/plain'][:10] == 'sys.flags('
|
| 206 |
+
assert 'text/latex' not in app.user_ns['b']
|
| 207 |
+
assert app.user_ns['c']['text/plain'] == \
|
| 208 |
+
"""\
|
| 209 |
+
[1] \n\
|
| 210 |
+
([ ],)
|
| 211 |
+
[2] \
|
| 212 |
+
"""
|
| 213 |
+
assert app.user_ns['c']['text/latex'] == '$\\displaystyle \\left( \\left[\\begin{matrix}1\\\\2\\end{matrix}\\right],\\right)$'
|
| 214 |
+
else:
|
| 215 |
+
assert app.user_ns['a'][0]['text/plain'] == '(True, False)'
|
| 216 |
+
assert 'text/latex' not in app.user_ns['a'][0]
|
| 217 |
+
assert app.user_ns['b'][0]['text/plain'][:10] == 'sys.flags('
|
| 218 |
+
assert 'text/latex' not in app.user_ns['b'][0]
|
| 219 |
+
assert app.user_ns['c'][0]['text/plain'] == \
|
| 220 |
+
"""\
|
| 221 |
+
[1] \n\
|
| 222 |
+
([ ],)
|
| 223 |
+
[2] \
|
| 224 |
+
"""
|
| 225 |
+
assert app.user_ns['c'][0]['text/latex'] == '$\\displaystyle \\left( \\left[\\begin{matrix}1\\\\2\\end{matrix}\\right],\\right)$'
|
| 226 |
+
|
| 227 |
+
def test_matplotlib_bad_latex():
|
| 228 |
+
# Initialize and setup IPython session
|
| 229 |
+
app = init_ipython_session()
|
| 230 |
+
app.run_cell("import IPython")
|
| 231 |
+
app.run_cell("ip = get_ipython()")
|
| 232 |
+
app.run_cell("inst = ip.instance()")
|
| 233 |
+
app.run_cell("format = inst.display_formatter.format")
|
| 234 |
+
app.run_cell("from sympy import init_printing, Matrix")
|
| 235 |
+
app.run_cell("init_printing(use_latex='matplotlib')")
|
| 236 |
+
|
| 237 |
+
# The png formatter is not enabled by default in this context
|
| 238 |
+
app.run_cell("inst.display_formatter.formatters['image/png'].enabled = True")
|
| 239 |
+
|
| 240 |
+
# Make sure no warnings are raised by IPython
|
| 241 |
+
app.run_cell("import warnings")
|
| 242 |
+
# IPython.core.formatters.FormatterWarning was introduced in IPython 2.0
|
| 243 |
+
if int(ipython.__version__.split(".")[0]) < 2:
|
| 244 |
+
app.run_cell("warnings.simplefilter('error')")
|
| 245 |
+
else:
|
| 246 |
+
app.run_cell("warnings.simplefilter('error', IPython.core.formatters.FormatterWarning)")
|
| 247 |
+
|
| 248 |
+
# This should not raise an exception
|
| 249 |
+
app.run_cell("a = format(Matrix([1, 2, 3]))")
|
| 250 |
+
|
| 251 |
+
# issue 9799
|
| 252 |
+
app.run_cell("from sympy import Piecewise, Symbol, Eq")
|
| 253 |
+
app.run_cell("x = Symbol('x'); pw = format(Piecewise((1, Eq(x, 0)), (0, True)))")
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def test_override_repr_latex():
|
| 257 |
+
# Initialize and setup IPython session
|
| 258 |
+
app = init_ipython_session()
|
| 259 |
+
app.run_cell("import IPython")
|
| 260 |
+
app.run_cell("ip = get_ipython()")
|
| 261 |
+
app.run_cell("inst = ip.instance()")
|
| 262 |
+
app.run_cell("format = inst.display_formatter.format")
|
| 263 |
+
app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
|
| 264 |
+
app.run_cell("from sympy import init_printing")
|
| 265 |
+
app.run_cell("from sympy import Symbol")
|
| 266 |
+
app.run_cell("init_printing(use_latex=True)")
|
| 267 |
+
app.run_cell("""\
|
| 268 |
+
class SymbolWithOverload(Symbol):
|
| 269 |
+
def _repr_latex_(self):
|
| 270 |
+
return r"Hello " + super()._repr_latex_() + " world"
|
| 271 |
+
""")
|
| 272 |
+
app.run_cell("a = format(SymbolWithOverload('s'))")
|
| 273 |
+
|
| 274 |
+
if int(ipython.__version__.split(".")[0]) < 1:
|
| 275 |
+
latex = app.user_ns['a']['text/latex']
|
| 276 |
+
else:
|
| 277 |
+
latex = app.user_ns['a'][0]['text/latex']
|
| 278 |
+
assert latex == r'Hello $\displaystyle s$ world'
|
lib/python3.12/site-packages/sympy/interactive/traversal.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.basic import Basic
|
| 2 |
+
from sympy.printing import pprint
|
| 3 |
+
|
| 4 |
+
import random
|
| 5 |
+
|
| 6 |
+
def interactive_traversal(expr):
|
| 7 |
+
"""Traverse a tree asking a user which branch to choose. """
|
| 8 |
+
|
| 9 |
+
RED, BRED = '\033[0;31m', '\033[1;31m'
|
| 10 |
+
GREEN, BGREEN = '\033[0;32m', '\033[1;32m'
|
| 11 |
+
YELLOW, BYELLOW = '\033[0;33m', '\033[1;33m' # noqa
|
| 12 |
+
BLUE, BBLUE = '\033[0;34m', '\033[1;34m' # noqa
|
| 13 |
+
MAGENTA, BMAGENTA = '\033[0;35m', '\033[1;35m'# noqa
|
| 14 |
+
CYAN, BCYAN = '\033[0;36m', '\033[1;36m' # noqa
|
| 15 |
+
END = '\033[0m'
|
| 16 |
+
|
| 17 |
+
def cprint(*args):
|
| 18 |
+
print("".join(map(str, args)) + END)
|
| 19 |
+
|
| 20 |
+
def _interactive_traversal(expr, stage):
|
| 21 |
+
if stage > 0:
|
| 22 |
+
print()
|
| 23 |
+
|
| 24 |
+
cprint("Current expression (stage ", BYELLOW, stage, END, "):")
|
| 25 |
+
print(BCYAN)
|
| 26 |
+
pprint(expr)
|
| 27 |
+
print(END)
|
| 28 |
+
|
| 29 |
+
if isinstance(expr, Basic):
|
| 30 |
+
if expr.is_Add:
|
| 31 |
+
args = expr.as_ordered_terms()
|
| 32 |
+
elif expr.is_Mul:
|
| 33 |
+
args = expr.as_ordered_factors()
|
| 34 |
+
else:
|
| 35 |
+
args = expr.args
|
| 36 |
+
elif hasattr(expr, "__iter__"):
|
| 37 |
+
args = list(expr)
|
| 38 |
+
else:
|
| 39 |
+
return expr
|
| 40 |
+
|
| 41 |
+
n_args = len(args)
|
| 42 |
+
|
| 43 |
+
if not n_args:
|
| 44 |
+
return expr
|
| 45 |
+
|
| 46 |
+
for i, arg in enumerate(args):
|
| 47 |
+
cprint(GREEN, "[", BGREEN, i, GREEN, "] ", BLUE, type(arg), END)
|
| 48 |
+
pprint(arg)
|
| 49 |
+
print()
|
| 50 |
+
|
| 51 |
+
if n_args == 1:
|
| 52 |
+
choices = '0'
|
| 53 |
+
else:
|
| 54 |
+
choices = '0-%d' % (n_args - 1)
|
| 55 |
+
|
| 56 |
+
try:
|
| 57 |
+
choice = input("Your choice [%s,f,l,r,d,?]: " % choices)
|
| 58 |
+
except EOFError:
|
| 59 |
+
result = expr
|
| 60 |
+
print()
|
| 61 |
+
else:
|
| 62 |
+
if choice == '?':
|
| 63 |
+
cprint(RED, "%s - select subexpression with the given index" %
|
| 64 |
+
choices)
|
| 65 |
+
cprint(RED, "f - select the first subexpression")
|
| 66 |
+
cprint(RED, "l - select the last subexpression")
|
| 67 |
+
cprint(RED, "r - select a random subexpression")
|
| 68 |
+
cprint(RED, "d - done\n")
|
| 69 |
+
|
| 70 |
+
result = _interactive_traversal(expr, stage)
|
| 71 |
+
elif choice in ('d', ''):
|
| 72 |
+
result = expr
|
| 73 |
+
elif choice == 'f':
|
| 74 |
+
result = _interactive_traversal(args[0], stage + 1)
|
| 75 |
+
elif choice == 'l':
|
| 76 |
+
result = _interactive_traversal(args[-1], stage + 1)
|
| 77 |
+
elif choice == 'r':
|
| 78 |
+
result = _interactive_traversal(random.choice(args), stage + 1)
|
| 79 |
+
else:
|
| 80 |
+
try:
|
| 81 |
+
choice = int(choice)
|
| 82 |
+
except ValueError:
|
| 83 |
+
cprint(BRED,
|
| 84 |
+
"Choice must be a number in %s range\n" % choices)
|
| 85 |
+
result = _interactive_traversal(expr, stage)
|
| 86 |
+
else:
|
| 87 |
+
if choice < 0 or choice >= n_args:
|
| 88 |
+
cprint(BRED, "Choice must be in %s range\n" % choices)
|
| 89 |
+
result = _interactive_traversal(expr, stage)
|
| 90 |
+
else:
|
| 91 |
+
result = _interactive_traversal(args[choice], stage + 1)
|
| 92 |
+
|
| 93 |
+
return result
|
| 94 |
+
|
| 95 |
+
return _interactive_traversal(expr, 0)
|
lib/python3.12/site-packages/sympy/plotting/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (630 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/__pycache__/experimental_lambdify.cpython-312.pyc
ADDED
|
Binary file (20.6 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/__pycache__/plot.cpython-312.pyc
ADDED
|
Binary file (43.7 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/__pycache__/plot_implicit.cpython-312.pyc
ADDED
|
Binary file (8.51 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/__pycache__/plotgrid.cpython-312.pyc
ADDED
|
Binary file (8.34 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/__pycache__/textplot.cpython-312.pyc
ADDED
|
Binary file (7.05 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (14.4 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .interval_arithmetic import interval
|
| 2 |
+
from .lib_interval import (Abs, exp, log, log10, sin, cos, tan, sqrt,
|
| 3 |
+
imin, imax, sinh, cosh, tanh, acosh, asinh, atanh,
|
| 4 |
+
asin, acos, atan, ceil, floor, And, Or)
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
'interval',
|
| 8 |
+
|
| 9 |
+
'Abs', 'exp', 'log', 'log10', 'sin', 'cos', 'tan', 'sqrt', 'imin', 'imax',
|
| 10 |
+
'sinh', 'cosh', 'tanh', 'acosh', 'asinh', 'atanh', 'asin', 'acos', 'atan',
|
| 11 |
+
'ceil', 'floor', 'And', 'Or',
|
| 12 |
+
]
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (814 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/interval_arithmetic.cpython-312.pyc
ADDED
|
Binary file (19.1 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/interval_membership.cpython-312.pyc
ADDED
|
Binary file (4.03 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/__pycache__/lib_interval.cpython-312.pyc
ADDED
|
Binary file (22.7 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Interval Arithmetic for plotting.
|
| 3 |
+
This module does not implement interval arithmetic accurately and
|
| 4 |
+
hence cannot be used for purposes other than plotting. If you want
|
| 5 |
+
to use interval arithmetic, use mpmath's interval arithmetic.
|
| 6 |
+
|
| 7 |
+
The module implements interval arithmetic using numpy and
|
| 8 |
+
python floating points. The rounding up and down is not handled
|
| 9 |
+
and hence this is not an accurate implementation of interval
|
| 10 |
+
arithmetic.
|
| 11 |
+
|
| 12 |
+
The module uses numpy for speed which cannot be achieved with mpmath.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
# Q: Why use numpy? Why not simply use mpmath's interval arithmetic?
|
| 16 |
+
# A: mpmath's interval arithmetic simulates a floating point unit
|
| 17 |
+
# and hence is slow, while numpy evaluations are orders of magnitude
|
| 18 |
+
# faster.
|
| 19 |
+
|
| 20 |
+
# Q: Why create a separate class for intervals? Why not use SymPy's
|
| 21 |
+
# Interval Sets?
|
| 22 |
+
# A: The functionalities that will be required for plotting is quite
|
| 23 |
+
# different from what Interval Sets implement.
|
| 24 |
+
|
| 25 |
+
# Q: Why is rounding up and down according to IEEE754 not handled?
|
| 26 |
+
# A: It is not possible to do it in both numpy and python. An external
|
| 27 |
+
# library has to used, which defeats the whole purpose i.e., speed. Also
|
| 28 |
+
# rounding is handled for very few functions in those libraries.
|
| 29 |
+
|
| 30 |
+
# Q Will my plots be affected?
|
| 31 |
+
# A It will not affect most of the plots. The interval arithmetic
|
| 32 |
+
# module based suffers the same problems as that of floating point
|
| 33 |
+
# arithmetic.
|
| 34 |
+
|
| 35 |
+
from sympy.core.numbers import int_valued
|
| 36 |
+
from sympy.core.logic import fuzzy_and
|
| 37 |
+
from sympy.simplify.simplify import nsimplify
|
| 38 |
+
|
| 39 |
+
from .interval_membership import intervalMembership
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class interval:
|
| 43 |
+
""" Represents an interval containing floating points as start and
|
| 44 |
+
end of the interval
|
| 45 |
+
The is_valid variable tracks whether the interval obtained as the
|
| 46 |
+
result of the function is in the domain and is continuous.
|
| 47 |
+
- True: Represents the interval result of a function is continuous and
|
| 48 |
+
in the domain of the function.
|
| 49 |
+
- False: The interval argument of the function was not in the domain of
|
| 50 |
+
the function, hence the is_valid of the result interval is False
|
| 51 |
+
- None: The function was not continuous over the interval or
|
| 52 |
+
the function's argument interval is partly in the domain of the
|
| 53 |
+
function
|
| 54 |
+
|
| 55 |
+
A comparison between an interval and a real number, or a
|
| 56 |
+
comparison between two intervals may return ``intervalMembership``
|
| 57 |
+
of two 3-valued logic values.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
def __init__(self, *args, is_valid=True, **kwargs):
|
| 61 |
+
self.is_valid = is_valid
|
| 62 |
+
if len(args) == 1:
|
| 63 |
+
if isinstance(args[0], interval):
|
| 64 |
+
self.start, self.end = args[0].start, args[0].end
|
| 65 |
+
else:
|
| 66 |
+
self.start = float(args[0])
|
| 67 |
+
self.end = float(args[0])
|
| 68 |
+
elif len(args) == 2:
|
| 69 |
+
if args[0] < args[1]:
|
| 70 |
+
self.start = float(args[0])
|
| 71 |
+
self.end = float(args[1])
|
| 72 |
+
else:
|
| 73 |
+
self.start = float(args[1])
|
| 74 |
+
self.end = float(args[0])
|
| 75 |
+
|
| 76 |
+
else:
|
| 77 |
+
raise ValueError("interval takes a maximum of two float values "
|
| 78 |
+
"as arguments")
|
| 79 |
+
|
| 80 |
+
@property
|
| 81 |
+
def mid(self):
|
| 82 |
+
return (self.start + self.end) / 2.0
|
| 83 |
+
|
| 84 |
+
@property
|
| 85 |
+
def width(self):
|
| 86 |
+
return self.end - self.start
|
| 87 |
+
|
| 88 |
+
def __repr__(self):
|
| 89 |
+
return "interval(%f, %f)" % (self.start, self.end)
|
| 90 |
+
|
| 91 |
+
def __str__(self):
|
| 92 |
+
return "[%f, %f]" % (self.start, self.end)
|
| 93 |
+
|
| 94 |
+
def __lt__(self, other):
|
| 95 |
+
if isinstance(other, (int, float)):
|
| 96 |
+
if self.end < other:
|
| 97 |
+
return intervalMembership(True, self.is_valid)
|
| 98 |
+
elif self.start > other:
|
| 99 |
+
return intervalMembership(False, self.is_valid)
|
| 100 |
+
else:
|
| 101 |
+
return intervalMembership(None, self.is_valid)
|
| 102 |
+
|
| 103 |
+
elif isinstance(other, interval):
|
| 104 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 105 |
+
if self.end < other. start:
|
| 106 |
+
return intervalMembership(True, valid)
|
| 107 |
+
if self.start > other.end:
|
| 108 |
+
return intervalMembership(False, valid)
|
| 109 |
+
return intervalMembership(None, valid)
|
| 110 |
+
else:
|
| 111 |
+
return NotImplemented
|
| 112 |
+
|
| 113 |
+
def __gt__(self, other):
|
| 114 |
+
if isinstance(other, (int, float)):
|
| 115 |
+
if self.start > other:
|
| 116 |
+
return intervalMembership(True, self.is_valid)
|
| 117 |
+
elif self.end < other:
|
| 118 |
+
return intervalMembership(False, self.is_valid)
|
| 119 |
+
else:
|
| 120 |
+
return intervalMembership(None, self.is_valid)
|
| 121 |
+
elif isinstance(other, interval):
|
| 122 |
+
return other.__lt__(self)
|
| 123 |
+
else:
|
| 124 |
+
return NotImplemented
|
| 125 |
+
|
| 126 |
+
def __eq__(self, other):
|
| 127 |
+
if isinstance(other, (int, float)):
|
| 128 |
+
if self.start == other and self.end == other:
|
| 129 |
+
return intervalMembership(True, self.is_valid)
|
| 130 |
+
if other in self:
|
| 131 |
+
return intervalMembership(None, self.is_valid)
|
| 132 |
+
else:
|
| 133 |
+
return intervalMembership(False, self.is_valid)
|
| 134 |
+
|
| 135 |
+
if isinstance(other, interval):
|
| 136 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 137 |
+
if self.start == other.start and self.end == other.end:
|
| 138 |
+
return intervalMembership(True, valid)
|
| 139 |
+
elif self.__lt__(other)[0] is not None:
|
| 140 |
+
return intervalMembership(False, valid)
|
| 141 |
+
else:
|
| 142 |
+
return intervalMembership(None, valid)
|
| 143 |
+
else:
|
| 144 |
+
return NotImplemented
|
| 145 |
+
|
| 146 |
+
def __ne__(self, other):
|
| 147 |
+
if isinstance(other, (int, float)):
|
| 148 |
+
if self.start == other and self.end == other:
|
| 149 |
+
return intervalMembership(False, self.is_valid)
|
| 150 |
+
if other in self:
|
| 151 |
+
return intervalMembership(None, self.is_valid)
|
| 152 |
+
else:
|
| 153 |
+
return intervalMembership(True, self.is_valid)
|
| 154 |
+
|
| 155 |
+
if isinstance(other, interval):
|
| 156 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 157 |
+
if self.start == other.start and self.end == other.end:
|
| 158 |
+
return intervalMembership(False, valid)
|
| 159 |
+
if not self.__lt__(other)[0] is None:
|
| 160 |
+
return intervalMembership(True, valid)
|
| 161 |
+
return intervalMembership(None, valid)
|
| 162 |
+
else:
|
| 163 |
+
return NotImplemented
|
| 164 |
+
|
| 165 |
+
def __le__(self, other):
|
| 166 |
+
if isinstance(other, (int, float)):
|
| 167 |
+
if self.end <= other:
|
| 168 |
+
return intervalMembership(True, self.is_valid)
|
| 169 |
+
if self.start > other:
|
| 170 |
+
return intervalMembership(False, self.is_valid)
|
| 171 |
+
else:
|
| 172 |
+
return intervalMembership(None, self.is_valid)
|
| 173 |
+
|
| 174 |
+
if isinstance(other, interval):
|
| 175 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 176 |
+
if self.end <= other.start:
|
| 177 |
+
return intervalMembership(True, valid)
|
| 178 |
+
if self.start > other.end:
|
| 179 |
+
return intervalMembership(False, valid)
|
| 180 |
+
return intervalMembership(None, valid)
|
| 181 |
+
else:
|
| 182 |
+
return NotImplemented
|
| 183 |
+
|
| 184 |
+
def __ge__(self, other):
|
| 185 |
+
if isinstance(other, (int, float)):
|
| 186 |
+
if self.start >= other:
|
| 187 |
+
return intervalMembership(True, self.is_valid)
|
| 188 |
+
elif self.end < other:
|
| 189 |
+
return intervalMembership(False, self.is_valid)
|
| 190 |
+
else:
|
| 191 |
+
return intervalMembership(None, self.is_valid)
|
| 192 |
+
elif isinstance(other, interval):
|
| 193 |
+
return other.__le__(self)
|
| 194 |
+
|
| 195 |
+
def __add__(self, other):
|
| 196 |
+
if isinstance(other, (int, float)):
|
| 197 |
+
if self.is_valid:
|
| 198 |
+
return interval(self.start + other, self.end + other)
|
| 199 |
+
else:
|
| 200 |
+
start = self.start + other
|
| 201 |
+
end = self.end + other
|
| 202 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 203 |
+
|
| 204 |
+
elif isinstance(other, interval):
|
| 205 |
+
start = self.start + other.start
|
| 206 |
+
end = self.end + other.end
|
| 207 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 208 |
+
return interval(start, end, is_valid=valid)
|
| 209 |
+
else:
|
| 210 |
+
return NotImplemented
|
| 211 |
+
|
| 212 |
+
__radd__ = __add__
|
| 213 |
+
|
| 214 |
+
def __sub__(self, other):
|
| 215 |
+
if isinstance(other, (int, float)):
|
| 216 |
+
start = self.start - other
|
| 217 |
+
end = self.end - other
|
| 218 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 219 |
+
|
| 220 |
+
elif isinstance(other, interval):
|
| 221 |
+
start = self.start - other.end
|
| 222 |
+
end = self.end - other.start
|
| 223 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 224 |
+
return interval(start, end, is_valid=valid)
|
| 225 |
+
else:
|
| 226 |
+
return NotImplemented
|
| 227 |
+
|
| 228 |
+
def __rsub__(self, other):
|
| 229 |
+
if isinstance(other, (int, float)):
|
| 230 |
+
start = other - self.end
|
| 231 |
+
end = other - self.start
|
| 232 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 233 |
+
elif isinstance(other, interval):
|
| 234 |
+
return other.__sub__(self)
|
| 235 |
+
else:
|
| 236 |
+
return NotImplemented
|
| 237 |
+
|
| 238 |
+
def __neg__(self):
|
| 239 |
+
if self.is_valid:
|
| 240 |
+
return interval(-self.end, -self.start)
|
| 241 |
+
else:
|
| 242 |
+
return interval(-self.end, -self.start, is_valid=self.is_valid)
|
| 243 |
+
|
| 244 |
+
def __mul__(self, other):
|
| 245 |
+
if isinstance(other, interval):
|
| 246 |
+
if self.is_valid is False or other.is_valid is False:
|
| 247 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 248 |
+
elif self.is_valid is None or other.is_valid is None:
|
| 249 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 250 |
+
else:
|
| 251 |
+
inters = []
|
| 252 |
+
inters.append(self.start * other.start)
|
| 253 |
+
inters.append(self.end * other.start)
|
| 254 |
+
inters.append(self.start * other.end)
|
| 255 |
+
inters.append(self.end * other.end)
|
| 256 |
+
start = min(inters)
|
| 257 |
+
end = max(inters)
|
| 258 |
+
return interval(start, end)
|
| 259 |
+
elif isinstance(other, (int, float)):
|
| 260 |
+
return interval(self.start*other, self.end*other, is_valid=self.is_valid)
|
| 261 |
+
else:
|
| 262 |
+
return NotImplemented
|
| 263 |
+
|
| 264 |
+
__rmul__ = __mul__
|
| 265 |
+
|
| 266 |
+
def __contains__(self, other):
|
| 267 |
+
if isinstance(other, (int, float)):
|
| 268 |
+
return self.start <= other and self.end >= other
|
| 269 |
+
else:
|
| 270 |
+
return self.start <= other.start and other.end <= self.end
|
| 271 |
+
|
| 272 |
+
def __rtruediv__(self, other):
|
| 273 |
+
if isinstance(other, (int, float)):
|
| 274 |
+
other = interval(other)
|
| 275 |
+
return other.__truediv__(self)
|
| 276 |
+
elif isinstance(other, interval):
|
| 277 |
+
return other.__truediv__(self)
|
| 278 |
+
else:
|
| 279 |
+
return NotImplemented
|
| 280 |
+
|
| 281 |
+
def __truediv__(self, other):
|
| 282 |
+
# Both None and False are handled
|
| 283 |
+
if not self.is_valid:
|
| 284 |
+
# Don't divide as the value is not valid
|
| 285 |
+
return interval(-float('inf'), float('inf'), is_valid=self.is_valid)
|
| 286 |
+
if isinstance(other, (int, float)):
|
| 287 |
+
if other == 0:
|
| 288 |
+
# Divide by zero encountered. valid nowhere
|
| 289 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 290 |
+
else:
|
| 291 |
+
return interval(self.start / other, self.end / other)
|
| 292 |
+
|
| 293 |
+
elif isinstance(other, interval):
|
| 294 |
+
if other.is_valid is False or self.is_valid is False:
|
| 295 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 296 |
+
elif other.is_valid is None or self.is_valid is None:
|
| 297 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 298 |
+
else:
|
| 299 |
+
# denominator contains both signs, i.e. being divided by zero
|
| 300 |
+
# return the whole real line with is_valid = None
|
| 301 |
+
if 0 in other:
|
| 302 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 303 |
+
|
| 304 |
+
# denominator negative
|
| 305 |
+
this = self
|
| 306 |
+
if other.end < 0:
|
| 307 |
+
this = -this
|
| 308 |
+
other = -other
|
| 309 |
+
|
| 310 |
+
# denominator positive
|
| 311 |
+
inters = []
|
| 312 |
+
inters.append(this.start / other.start)
|
| 313 |
+
inters.append(this.end / other.start)
|
| 314 |
+
inters.append(this.start / other.end)
|
| 315 |
+
inters.append(this.end / other.end)
|
| 316 |
+
start = max(inters)
|
| 317 |
+
end = min(inters)
|
| 318 |
+
return interval(start, end)
|
| 319 |
+
else:
|
| 320 |
+
return NotImplemented
|
| 321 |
+
|
| 322 |
+
def __pow__(self, other):
|
| 323 |
+
# Implements only power to an integer.
|
| 324 |
+
from .lib_interval import exp, log
|
| 325 |
+
if not self.is_valid:
|
| 326 |
+
return self
|
| 327 |
+
if isinstance(other, interval):
|
| 328 |
+
return exp(other * log(self))
|
| 329 |
+
elif isinstance(other, (float, int)):
|
| 330 |
+
if other < 0:
|
| 331 |
+
return 1 / self.__pow__(abs(other))
|
| 332 |
+
else:
|
| 333 |
+
if int_valued(other):
|
| 334 |
+
return _pow_int(self, other)
|
| 335 |
+
else:
|
| 336 |
+
return _pow_float(self, other)
|
| 337 |
+
else:
|
| 338 |
+
return NotImplemented
|
| 339 |
+
|
| 340 |
+
def __rpow__(self, other):
|
| 341 |
+
if isinstance(other, (float, int)):
|
| 342 |
+
if not self.is_valid:
|
| 343 |
+
#Don't do anything
|
| 344 |
+
return self
|
| 345 |
+
elif other < 0:
|
| 346 |
+
if self.width > 0:
|
| 347 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 348 |
+
else:
|
| 349 |
+
power_rational = nsimplify(self.start)
|
| 350 |
+
num, denom = power_rational.as_numer_denom()
|
| 351 |
+
if denom % 2 == 0:
|
| 352 |
+
return interval(-float('inf'), float('inf'),
|
| 353 |
+
is_valid=False)
|
| 354 |
+
else:
|
| 355 |
+
start = -abs(other)**self.start
|
| 356 |
+
end = start
|
| 357 |
+
return interval(start, end)
|
| 358 |
+
else:
|
| 359 |
+
return interval(other**self.start, other**self.end)
|
| 360 |
+
elif isinstance(other, interval):
|
| 361 |
+
return other.__pow__(self)
|
| 362 |
+
else:
|
| 363 |
+
return NotImplemented
|
| 364 |
+
|
| 365 |
+
def __hash__(self):
|
| 366 |
+
return hash((self.is_valid, self.start, self.end))
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
def _pow_float(inter, power):
|
| 370 |
+
"""Evaluates an interval raised to a floating point."""
|
| 371 |
+
power_rational = nsimplify(power)
|
| 372 |
+
num, denom = power_rational.as_numer_denom()
|
| 373 |
+
if num % 2 == 0:
|
| 374 |
+
start = abs(inter.start)**power
|
| 375 |
+
end = abs(inter.end)**power
|
| 376 |
+
if start < 0:
|
| 377 |
+
ret = interval(0, max(start, end))
|
| 378 |
+
else:
|
| 379 |
+
ret = interval(start, end)
|
| 380 |
+
return ret
|
| 381 |
+
elif denom % 2 == 0:
|
| 382 |
+
if inter.end < 0:
|
| 383 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 384 |
+
elif inter.start < 0:
|
| 385 |
+
return interval(0, inter.end**power, is_valid=None)
|
| 386 |
+
else:
|
| 387 |
+
return interval(inter.start**power, inter.end**power)
|
| 388 |
+
else:
|
| 389 |
+
if inter.start < 0:
|
| 390 |
+
start = -abs(inter.start)**power
|
| 391 |
+
else:
|
| 392 |
+
start = inter.start**power
|
| 393 |
+
|
| 394 |
+
if inter.end < 0:
|
| 395 |
+
end = -abs(inter.end)**power
|
| 396 |
+
else:
|
| 397 |
+
end = inter.end**power
|
| 398 |
+
|
| 399 |
+
return interval(start, end, is_valid=inter.is_valid)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
def _pow_int(inter, power):
|
| 403 |
+
"""Evaluates an interval raised to an integer power"""
|
| 404 |
+
power = int(power)
|
| 405 |
+
if power & 1:
|
| 406 |
+
return interval(inter.start**power, inter.end**power)
|
| 407 |
+
else:
|
| 408 |
+
if inter.start < 0 and inter.end > 0:
|
| 409 |
+
start = 0
|
| 410 |
+
end = max(inter.start**power, inter.end**power)
|
| 411 |
+
return interval(start, end)
|
| 412 |
+
else:
|
| 413 |
+
return interval(inter.start**power, inter.end**power)
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/interval_membership.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.logic import fuzzy_and, fuzzy_or, fuzzy_not, fuzzy_xor
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class intervalMembership:
|
| 5 |
+
"""Represents a boolean expression returned by the comparison of
|
| 6 |
+
the interval object.
|
| 7 |
+
|
| 8 |
+
Parameters
|
| 9 |
+
==========
|
| 10 |
+
|
| 11 |
+
(a, b) : (bool, bool)
|
| 12 |
+
The first value determines the comparison as follows:
|
| 13 |
+
- True: If the comparison is True throughout the intervals.
|
| 14 |
+
- False: If the comparison is False throughout the intervals.
|
| 15 |
+
- None: If the comparison is True for some part of the intervals.
|
| 16 |
+
|
| 17 |
+
The second value is determined as follows:
|
| 18 |
+
- True: If both the intervals in comparison are valid.
|
| 19 |
+
- False: If at least one of the intervals is False, else
|
| 20 |
+
- None
|
| 21 |
+
"""
|
| 22 |
+
def __init__(self, a, b):
|
| 23 |
+
self._wrapped = (a, b)
|
| 24 |
+
|
| 25 |
+
def __getitem__(self, i):
|
| 26 |
+
try:
|
| 27 |
+
return self._wrapped[i]
|
| 28 |
+
except IndexError:
|
| 29 |
+
raise IndexError(
|
| 30 |
+
"{} must be a valid indexing for the 2-tuple."
|
| 31 |
+
.format(i))
|
| 32 |
+
|
| 33 |
+
def __len__(self):
|
| 34 |
+
return 2
|
| 35 |
+
|
| 36 |
+
def __iter__(self):
|
| 37 |
+
return iter(self._wrapped)
|
| 38 |
+
|
| 39 |
+
def __str__(self):
|
| 40 |
+
return "intervalMembership({}, {})".format(*self)
|
| 41 |
+
__repr__ = __str__
|
| 42 |
+
|
| 43 |
+
def __and__(self, other):
|
| 44 |
+
if not isinstance(other, intervalMembership):
|
| 45 |
+
raise ValueError(
|
| 46 |
+
"The comparison is not supported for {}.".format(other))
|
| 47 |
+
|
| 48 |
+
a1, b1 = self
|
| 49 |
+
a2, b2 = other
|
| 50 |
+
return intervalMembership(fuzzy_and([a1, a2]), fuzzy_and([b1, b2]))
|
| 51 |
+
|
| 52 |
+
def __or__(self, other):
|
| 53 |
+
if not isinstance(other, intervalMembership):
|
| 54 |
+
raise ValueError(
|
| 55 |
+
"The comparison is not supported for {}.".format(other))
|
| 56 |
+
|
| 57 |
+
a1, b1 = self
|
| 58 |
+
a2, b2 = other
|
| 59 |
+
return intervalMembership(fuzzy_or([a1, a2]), fuzzy_and([b1, b2]))
|
| 60 |
+
|
| 61 |
+
def __invert__(self):
|
| 62 |
+
a, b = self
|
| 63 |
+
return intervalMembership(fuzzy_not(a), b)
|
| 64 |
+
|
| 65 |
+
def __xor__(self, other):
|
| 66 |
+
if not isinstance(other, intervalMembership):
|
| 67 |
+
raise ValueError(
|
| 68 |
+
"The comparison is not supported for {}.".format(other))
|
| 69 |
+
|
| 70 |
+
a1, b1 = self
|
| 71 |
+
a2, b2 = other
|
| 72 |
+
return intervalMembership(fuzzy_xor([a1, a2]), fuzzy_and([b1, b2]))
|
| 73 |
+
|
| 74 |
+
def __eq__(self, other):
|
| 75 |
+
return self._wrapped == other
|
| 76 |
+
|
| 77 |
+
def __ne__(self, other):
|
| 78 |
+
return self._wrapped != other
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/lib_interval.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" The module contains implemented functions for interval arithmetic."""
|
| 2 |
+
from functools import reduce
|
| 3 |
+
|
| 4 |
+
from sympy.plotting.intervalmath import interval
|
| 5 |
+
from sympy.external import import_module
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def Abs(x):
|
| 9 |
+
if isinstance(x, (int, float)):
|
| 10 |
+
return interval(abs(x))
|
| 11 |
+
elif isinstance(x, interval):
|
| 12 |
+
if x.start < 0 and x.end > 0:
|
| 13 |
+
return interval(0, max(abs(x.start), abs(x.end)), is_valid=x.is_valid)
|
| 14 |
+
else:
|
| 15 |
+
return interval(abs(x.start), abs(x.end))
|
| 16 |
+
else:
|
| 17 |
+
raise NotImplementedError
|
| 18 |
+
|
| 19 |
+
#Monotonic
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def exp(x):
|
| 23 |
+
"""evaluates the exponential of an interval"""
|
| 24 |
+
np = import_module('numpy')
|
| 25 |
+
if isinstance(x, (int, float)):
|
| 26 |
+
return interval(np.exp(x), np.exp(x))
|
| 27 |
+
elif isinstance(x, interval):
|
| 28 |
+
return interval(np.exp(x.start), np.exp(x.end), is_valid=x.is_valid)
|
| 29 |
+
else:
|
| 30 |
+
raise NotImplementedError
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
#Monotonic
|
| 34 |
+
def log(x):
|
| 35 |
+
"""evaluates the natural logarithm of an interval"""
|
| 36 |
+
np = import_module('numpy')
|
| 37 |
+
if isinstance(x, (int, float)):
|
| 38 |
+
if x <= 0:
|
| 39 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 40 |
+
else:
|
| 41 |
+
return interval(np.log(x))
|
| 42 |
+
elif isinstance(x, interval):
|
| 43 |
+
if not x.is_valid:
|
| 44 |
+
return interval(-np.inf, np.inf, is_valid=x.is_valid)
|
| 45 |
+
elif x.end <= 0:
|
| 46 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 47 |
+
elif x.start <= 0:
|
| 48 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 49 |
+
|
| 50 |
+
return interval(np.log(x.start), np.log(x.end))
|
| 51 |
+
else:
|
| 52 |
+
raise NotImplementedError
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
#Monotonic
|
| 56 |
+
def log10(x):
|
| 57 |
+
"""evaluates the logarithm to the base 10 of an interval"""
|
| 58 |
+
np = import_module('numpy')
|
| 59 |
+
if isinstance(x, (int, float)):
|
| 60 |
+
if x <= 0:
|
| 61 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 62 |
+
else:
|
| 63 |
+
return interval(np.log10(x))
|
| 64 |
+
elif isinstance(x, interval):
|
| 65 |
+
if not x.is_valid:
|
| 66 |
+
return interval(-np.inf, np.inf, is_valid=x.is_valid)
|
| 67 |
+
elif x.end <= 0:
|
| 68 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 69 |
+
elif x.start <= 0:
|
| 70 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 71 |
+
return interval(np.log10(x.start), np.log10(x.end))
|
| 72 |
+
else:
|
| 73 |
+
raise NotImplementedError
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
#Monotonic
|
| 77 |
+
def atan(x):
|
| 78 |
+
"""evaluates the tan inverse of an interval"""
|
| 79 |
+
np = import_module('numpy')
|
| 80 |
+
if isinstance(x, (int, float)):
|
| 81 |
+
return interval(np.arctan(x))
|
| 82 |
+
elif isinstance(x, interval):
|
| 83 |
+
start = np.arctan(x.start)
|
| 84 |
+
end = np.arctan(x.end)
|
| 85 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 86 |
+
else:
|
| 87 |
+
raise NotImplementedError
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
#periodic
|
| 91 |
+
def sin(x):
|
| 92 |
+
"""evaluates the sine of an interval"""
|
| 93 |
+
np = import_module('numpy')
|
| 94 |
+
if isinstance(x, (int, float)):
|
| 95 |
+
return interval(np.sin(x))
|
| 96 |
+
elif isinstance(x, interval):
|
| 97 |
+
if not x.is_valid:
|
| 98 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 99 |
+
na, __ = divmod(x.start, np.pi / 2.0)
|
| 100 |
+
nb, __ = divmod(x.end, np.pi / 2.0)
|
| 101 |
+
start = min(np.sin(x.start), np.sin(x.end))
|
| 102 |
+
end = max(np.sin(x.start), np.sin(x.end))
|
| 103 |
+
if nb - na > 4:
|
| 104 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 105 |
+
elif na == nb:
|
| 106 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 107 |
+
else:
|
| 108 |
+
if (na - 1) // 4 != (nb - 1) // 4:
|
| 109 |
+
#sin has max
|
| 110 |
+
end = 1
|
| 111 |
+
if (na - 3) // 4 != (nb - 3) // 4:
|
| 112 |
+
#sin has min
|
| 113 |
+
start = -1
|
| 114 |
+
return interval(start, end)
|
| 115 |
+
else:
|
| 116 |
+
raise NotImplementedError
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
#periodic
|
| 120 |
+
def cos(x):
|
| 121 |
+
"""Evaluates the cos of an interval"""
|
| 122 |
+
np = import_module('numpy')
|
| 123 |
+
if isinstance(x, (int, float)):
|
| 124 |
+
return interval(np.sin(x))
|
| 125 |
+
elif isinstance(x, interval):
|
| 126 |
+
if not (np.isfinite(x.start) and np.isfinite(x.end)):
|
| 127 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 128 |
+
na, __ = divmod(x.start, np.pi / 2.0)
|
| 129 |
+
nb, __ = divmod(x.end, np.pi / 2.0)
|
| 130 |
+
start = min(np.cos(x.start), np.cos(x.end))
|
| 131 |
+
end = max(np.cos(x.start), np.cos(x.end))
|
| 132 |
+
if nb - na > 4:
|
| 133 |
+
#differ more than 2*pi
|
| 134 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 135 |
+
elif na == nb:
|
| 136 |
+
#in the same quadarant
|
| 137 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 138 |
+
else:
|
| 139 |
+
if (na) // 4 != (nb) // 4:
|
| 140 |
+
#cos has max
|
| 141 |
+
end = 1
|
| 142 |
+
if (na - 2) // 4 != (nb - 2) // 4:
|
| 143 |
+
#cos has min
|
| 144 |
+
start = -1
|
| 145 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 146 |
+
else:
|
| 147 |
+
raise NotImplementedError
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def tan(x):
|
| 151 |
+
"""Evaluates the tan of an interval"""
|
| 152 |
+
return sin(x) / cos(x)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
#Monotonic
|
| 156 |
+
def sqrt(x):
|
| 157 |
+
"""Evaluates the square root of an interval"""
|
| 158 |
+
np = import_module('numpy')
|
| 159 |
+
if isinstance(x, (int, float)):
|
| 160 |
+
if x > 0:
|
| 161 |
+
return interval(np.sqrt(x))
|
| 162 |
+
else:
|
| 163 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 164 |
+
elif isinstance(x, interval):
|
| 165 |
+
#Outside the domain
|
| 166 |
+
if x.end < 0:
|
| 167 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 168 |
+
#Partially outside the domain
|
| 169 |
+
elif x.start < 0:
|
| 170 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 171 |
+
else:
|
| 172 |
+
return interval(np.sqrt(x.start), np.sqrt(x.end),
|
| 173 |
+
is_valid=x.is_valid)
|
| 174 |
+
else:
|
| 175 |
+
raise NotImplementedError
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def imin(*args):
|
| 179 |
+
"""Evaluates the minimum of a list of intervals"""
|
| 180 |
+
np = import_module('numpy')
|
| 181 |
+
if not all(isinstance(arg, (int, float, interval)) for arg in args):
|
| 182 |
+
return NotImplementedError
|
| 183 |
+
else:
|
| 184 |
+
new_args = [a for a in args if isinstance(a, (int, float))
|
| 185 |
+
or a.is_valid]
|
| 186 |
+
if len(new_args) == 0:
|
| 187 |
+
if all(a.is_valid is False for a in args):
|
| 188 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 189 |
+
else:
|
| 190 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 191 |
+
start_array = [a if isinstance(a, (int, float)) else a.start
|
| 192 |
+
for a in new_args]
|
| 193 |
+
|
| 194 |
+
end_array = [a if isinstance(a, (int, float)) else a.end
|
| 195 |
+
for a in new_args]
|
| 196 |
+
return interval(min(start_array), min(end_array))
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def imax(*args):
|
| 200 |
+
"""Evaluates the maximum of a list of intervals"""
|
| 201 |
+
np = import_module('numpy')
|
| 202 |
+
if not all(isinstance(arg, (int, float, interval)) for arg in args):
|
| 203 |
+
return NotImplementedError
|
| 204 |
+
else:
|
| 205 |
+
new_args = [a for a in args if isinstance(a, (int, float))
|
| 206 |
+
or a.is_valid]
|
| 207 |
+
if len(new_args) == 0:
|
| 208 |
+
if all(a.is_valid is False for a in args):
|
| 209 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 210 |
+
else:
|
| 211 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 212 |
+
start_array = [a if isinstance(a, (int, float)) else a.start
|
| 213 |
+
for a in new_args]
|
| 214 |
+
|
| 215 |
+
end_array = [a if isinstance(a, (int, float)) else a.end
|
| 216 |
+
for a in new_args]
|
| 217 |
+
|
| 218 |
+
return interval(max(start_array), max(end_array))
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
#Monotonic
|
| 222 |
+
def sinh(x):
|
| 223 |
+
"""Evaluates the hyperbolic sine of an interval"""
|
| 224 |
+
np = import_module('numpy')
|
| 225 |
+
if isinstance(x, (int, float)):
|
| 226 |
+
return interval(np.sinh(x), np.sinh(x))
|
| 227 |
+
elif isinstance(x, interval):
|
| 228 |
+
return interval(np.sinh(x.start), np.sinh(x.end), is_valid=x.is_valid)
|
| 229 |
+
else:
|
| 230 |
+
raise NotImplementedError
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def cosh(x):
|
| 234 |
+
"""Evaluates the hyperbolic cos of an interval"""
|
| 235 |
+
np = import_module('numpy')
|
| 236 |
+
if isinstance(x, (int, float)):
|
| 237 |
+
return interval(np.cosh(x), np.cosh(x))
|
| 238 |
+
elif isinstance(x, interval):
|
| 239 |
+
#both signs
|
| 240 |
+
if x.start < 0 and x.end > 0:
|
| 241 |
+
end = max(np.cosh(x.start), np.cosh(x.end))
|
| 242 |
+
return interval(1, end, is_valid=x.is_valid)
|
| 243 |
+
else:
|
| 244 |
+
#Monotonic
|
| 245 |
+
start = np.cosh(x.start)
|
| 246 |
+
end = np.cosh(x.end)
|
| 247 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 248 |
+
else:
|
| 249 |
+
raise NotImplementedError
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
#Monotonic
|
| 253 |
+
def tanh(x):
|
| 254 |
+
"""Evaluates the hyperbolic tan of an interval"""
|
| 255 |
+
np = import_module('numpy')
|
| 256 |
+
if isinstance(x, (int, float)):
|
| 257 |
+
return interval(np.tanh(x), np.tanh(x))
|
| 258 |
+
elif isinstance(x, interval):
|
| 259 |
+
return interval(np.tanh(x.start), np.tanh(x.end), is_valid=x.is_valid)
|
| 260 |
+
else:
|
| 261 |
+
raise NotImplementedError
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def asin(x):
|
| 265 |
+
"""Evaluates the inverse sine of an interval"""
|
| 266 |
+
np = import_module('numpy')
|
| 267 |
+
if isinstance(x, (int, float)):
|
| 268 |
+
#Outside the domain
|
| 269 |
+
if abs(x) > 1:
|
| 270 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 271 |
+
else:
|
| 272 |
+
return interval(np.arcsin(x), np.arcsin(x))
|
| 273 |
+
elif isinstance(x, interval):
|
| 274 |
+
#Outside the domain
|
| 275 |
+
if x.is_valid is False or x.start > 1 or x.end < -1:
|
| 276 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 277 |
+
#Partially outside the domain
|
| 278 |
+
elif x.start < -1 or x.end > 1:
|
| 279 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 280 |
+
else:
|
| 281 |
+
start = np.arcsin(x.start)
|
| 282 |
+
end = np.arcsin(x.end)
|
| 283 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def acos(x):
|
| 287 |
+
"""Evaluates the inverse cos of an interval"""
|
| 288 |
+
np = import_module('numpy')
|
| 289 |
+
if isinstance(x, (int, float)):
|
| 290 |
+
if abs(x) > 1:
|
| 291 |
+
#Outside the domain
|
| 292 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 293 |
+
else:
|
| 294 |
+
return interval(np.arccos(x), np.arccos(x))
|
| 295 |
+
elif isinstance(x, interval):
|
| 296 |
+
#Outside the domain
|
| 297 |
+
if x.is_valid is False or x.start > 1 or x.end < -1:
|
| 298 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 299 |
+
#Partially outside the domain
|
| 300 |
+
elif x.start < -1 or x.end > 1:
|
| 301 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 302 |
+
else:
|
| 303 |
+
start = np.arccos(x.start)
|
| 304 |
+
end = np.arccos(x.end)
|
| 305 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def ceil(x):
|
| 309 |
+
"""Evaluates the ceiling of an interval"""
|
| 310 |
+
np = import_module('numpy')
|
| 311 |
+
if isinstance(x, (int, float)):
|
| 312 |
+
return interval(np.ceil(x))
|
| 313 |
+
elif isinstance(x, interval):
|
| 314 |
+
if x.is_valid is False:
|
| 315 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 316 |
+
else:
|
| 317 |
+
start = np.ceil(x.start)
|
| 318 |
+
end = np.ceil(x.end)
|
| 319 |
+
#Continuous over the interval
|
| 320 |
+
if start == end:
|
| 321 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 322 |
+
else:
|
| 323 |
+
#Not continuous over the interval
|
| 324 |
+
return interval(start, end, is_valid=None)
|
| 325 |
+
else:
|
| 326 |
+
return NotImplementedError
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def floor(x):
|
| 330 |
+
"""Evaluates the floor of an interval"""
|
| 331 |
+
np = import_module('numpy')
|
| 332 |
+
if isinstance(x, (int, float)):
|
| 333 |
+
return interval(np.floor(x))
|
| 334 |
+
elif isinstance(x, interval):
|
| 335 |
+
if x.is_valid is False:
|
| 336 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 337 |
+
else:
|
| 338 |
+
start = np.floor(x.start)
|
| 339 |
+
end = np.floor(x.end)
|
| 340 |
+
#continuous over the argument
|
| 341 |
+
if start == end:
|
| 342 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 343 |
+
else:
|
| 344 |
+
#not continuous over the interval
|
| 345 |
+
return interval(start, end, is_valid=None)
|
| 346 |
+
else:
|
| 347 |
+
return NotImplementedError
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def acosh(x):
|
| 351 |
+
"""Evaluates the inverse hyperbolic cosine of an interval"""
|
| 352 |
+
np = import_module('numpy')
|
| 353 |
+
if isinstance(x, (int, float)):
|
| 354 |
+
#Outside the domain
|
| 355 |
+
if x < 1:
|
| 356 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 357 |
+
else:
|
| 358 |
+
return interval(np.arccosh(x))
|
| 359 |
+
elif isinstance(x, interval):
|
| 360 |
+
#Outside the domain
|
| 361 |
+
if x.end < 1:
|
| 362 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 363 |
+
#Partly outside the domain
|
| 364 |
+
elif x.start < 1:
|
| 365 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 366 |
+
else:
|
| 367 |
+
start = np.arccosh(x.start)
|
| 368 |
+
end = np.arccosh(x.end)
|
| 369 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 370 |
+
else:
|
| 371 |
+
return NotImplementedError
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
#Monotonic
|
| 375 |
+
def asinh(x):
|
| 376 |
+
"""Evaluates the inverse hyperbolic sine of an interval"""
|
| 377 |
+
np = import_module('numpy')
|
| 378 |
+
if isinstance(x, (int, float)):
|
| 379 |
+
return interval(np.arcsinh(x))
|
| 380 |
+
elif isinstance(x, interval):
|
| 381 |
+
start = np.arcsinh(x.start)
|
| 382 |
+
end = np.arcsinh(x.end)
|
| 383 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 384 |
+
else:
|
| 385 |
+
return NotImplementedError
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def atanh(x):
|
| 389 |
+
"""Evaluates the inverse hyperbolic tangent of an interval"""
|
| 390 |
+
np = import_module('numpy')
|
| 391 |
+
if isinstance(x, (int, float)):
|
| 392 |
+
#Outside the domain
|
| 393 |
+
if abs(x) >= 1:
|
| 394 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 395 |
+
else:
|
| 396 |
+
return interval(np.arctanh(x))
|
| 397 |
+
elif isinstance(x, interval):
|
| 398 |
+
#outside the domain
|
| 399 |
+
if x.is_valid is False or x.start >= 1 or x.end <= -1:
|
| 400 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 401 |
+
#partly outside the domain
|
| 402 |
+
elif x.start <= -1 or x.end >= 1:
|
| 403 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 404 |
+
else:
|
| 405 |
+
start = np.arctanh(x.start)
|
| 406 |
+
end = np.arctanh(x.end)
|
| 407 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 408 |
+
else:
|
| 409 |
+
return NotImplementedError
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
#Three valued logic for interval plotting.
|
| 413 |
+
|
| 414 |
+
def And(*args):
|
| 415 |
+
"""Defines the three valued ``And`` behaviour for a 2-tuple of
|
| 416 |
+
three valued logic values"""
|
| 417 |
+
def reduce_and(cmp_intervala, cmp_intervalb):
|
| 418 |
+
if cmp_intervala[0] is False or cmp_intervalb[0] is False:
|
| 419 |
+
first = False
|
| 420 |
+
elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
|
| 421 |
+
first = None
|
| 422 |
+
else:
|
| 423 |
+
first = True
|
| 424 |
+
if cmp_intervala[1] is False or cmp_intervalb[1] is False:
|
| 425 |
+
second = False
|
| 426 |
+
elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
|
| 427 |
+
second = None
|
| 428 |
+
else:
|
| 429 |
+
second = True
|
| 430 |
+
return (first, second)
|
| 431 |
+
return reduce(reduce_and, args)
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def Or(*args):
|
| 435 |
+
"""Defines the three valued ``Or`` behaviour for a 2-tuple of
|
| 436 |
+
three valued logic values"""
|
| 437 |
+
def reduce_or(cmp_intervala, cmp_intervalb):
|
| 438 |
+
if cmp_intervala[0] is True or cmp_intervalb[0] is True:
|
| 439 |
+
first = True
|
| 440 |
+
elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
|
| 441 |
+
first = None
|
| 442 |
+
else:
|
| 443 |
+
first = False
|
| 444 |
+
|
| 445 |
+
if cmp_intervala[1] is True or cmp_intervalb[1] is True:
|
| 446 |
+
second = True
|
| 447 |
+
elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
|
| 448 |
+
second = None
|
| 449 |
+
else:
|
| 450 |
+
second = False
|
| 451 |
+
return (first, second)
|
| 452 |
+
return reduce(reduce_or, args)
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (212 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/test_interval_functions.cpython-312.pyc
ADDED
|
Binary file (23.8 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/test_interval_membership.cpython-312.pyc
ADDED
|
Binary file (6.44 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/__pycache__/test_intervalmath.cpython-312.pyc
ADDED
|
Binary file (14 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/test_interval_functions.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.external import import_module
|
| 2 |
+
from sympy.plotting.intervalmath import (
|
| 3 |
+
Abs, acos, acosh, And, asin, asinh, atan, atanh, ceil, cos, cosh,
|
| 4 |
+
exp, floor, imax, imin, interval, log, log10, Or, sin, sinh, sqrt,
|
| 5 |
+
tan, tanh,
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
np = import_module('numpy')
|
| 9 |
+
if not np:
|
| 10 |
+
disabled = True
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
#requires Numpy. Hence included in interval_functions
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def test_interval_pow():
|
| 17 |
+
a = 2**interval(1, 2) == interval(2, 4)
|
| 18 |
+
assert a == (True, True)
|
| 19 |
+
a = interval(1, 2)**interval(1, 2) == interval(1, 4)
|
| 20 |
+
assert a == (True, True)
|
| 21 |
+
a = interval(-1, 1)**interval(0.5, 2)
|
| 22 |
+
assert a.is_valid is None
|
| 23 |
+
a = interval(-2, -1) ** interval(1, 2)
|
| 24 |
+
assert a.is_valid is False
|
| 25 |
+
a = interval(-2, -1) ** (1.0 / 2)
|
| 26 |
+
assert a.is_valid is False
|
| 27 |
+
a = interval(-1, 1)**(1.0 / 2)
|
| 28 |
+
assert a.is_valid is None
|
| 29 |
+
a = interval(-1, 1)**(1.0 / 3) == interval(-1, 1)
|
| 30 |
+
assert a == (True, True)
|
| 31 |
+
a = interval(-1, 1)**2 == interval(0, 1)
|
| 32 |
+
assert a == (True, True)
|
| 33 |
+
a = interval(-1, 1) ** (1.0 / 29) == interval(-1, 1)
|
| 34 |
+
assert a == (True, True)
|
| 35 |
+
a = -2**interval(1, 1) == interval(-2, -2)
|
| 36 |
+
assert a == (True, True)
|
| 37 |
+
|
| 38 |
+
a = interval(1, 2, is_valid=False)**2
|
| 39 |
+
assert a.is_valid is False
|
| 40 |
+
|
| 41 |
+
a = (-3)**interval(1, 2)
|
| 42 |
+
assert a.is_valid is False
|
| 43 |
+
a = (-4)**interval(0.5, 0.5)
|
| 44 |
+
assert a.is_valid is False
|
| 45 |
+
assert ((-3)**interval(1, 1) == interval(-3, -3)) == (True, True)
|
| 46 |
+
|
| 47 |
+
a = interval(8, 64)**(2.0 / 3)
|
| 48 |
+
assert abs(a.start - 4) < 1e-10 # eps
|
| 49 |
+
assert abs(a.end - 16) < 1e-10
|
| 50 |
+
a = interval(-8, 64)**(2.0 / 3)
|
| 51 |
+
assert abs(a.start - 4) < 1e-10 # eps
|
| 52 |
+
assert abs(a.end - 16) < 1e-10
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def test_exp():
|
| 56 |
+
a = exp(interval(-np.inf, 0))
|
| 57 |
+
assert a.start == np.exp(-np.inf)
|
| 58 |
+
assert a.end == np.exp(0)
|
| 59 |
+
a = exp(interval(1, 2))
|
| 60 |
+
assert a.start == np.exp(1)
|
| 61 |
+
assert a.end == np.exp(2)
|
| 62 |
+
a = exp(1)
|
| 63 |
+
assert a.start == np.exp(1)
|
| 64 |
+
assert a.end == np.exp(1)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def test_log():
|
| 68 |
+
a = log(interval(1, 2))
|
| 69 |
+
assert a.start == 0
|
| 70 |
+
assert a.end == np.log(2)
|
| 71 |
+
a = log(interval(-1, 1))
|
| 72 |
+
assert a.is_valid is None
|
| 73 |
+
a = log(interval(-3, -1))
|
| 74 |
+
assert a.is_valid is False
|
| 75 |
+
a = log(-3)
|
| 76 |
+
assert a.is_valid is False
|
| 77 |
+
a = log(2)
|
| 78 |
+
assert a.start == np.log(2)
|
| 79 |
+
assert a.end == np.log(2)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def test_log10():
|
| 83 |
+
a = log10(interval(1, 2))
|
| 84 |
+
assert a.start == 0
|
| 85 |
+
assert a.end == np.log10(2)
|
| 86 |
+
a = log10(interval(-1, 1))
|
| 87 |
+
assert a.is_valid is None
|
| 88 |
+
a = log10(interval(-3, -1))
|
| 89 |
+
assert a.is_valid is False
|
| 90 |
+
a = log10(-3)
|
| 91 |
+
assert a.is_valid is False
|
| 92 |
+
a = log10(2)
|
| 93 |
+
assert a.start == np.log10(2)
|
| 94 |
+
assert a.end == np.log10(2)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def test_atan():
|
| 98 |
+
a = atan(interval(0, 1))
|
| 99 |
+
assert a.start == np.arctan(0)
|
| 100 |
+
assert a.end == np.arctan(1)
|
| 101 |
+
a = atan(1)
|
| 102 |
+
assert a.start == np.arctan(1)
|
| 103 |
+
assert a.end == np.arctan(1)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def test_sin():
|
| 107 |
+
a = sin(interval(0, np.pi / 4))
|
| 108 |
+
assert a.start == np.sin(0)
|
| 109 |
+
assert a.end == np.sin(np.pi / 4)
|
| 110 |
+
|
| 111 |
+
a = sin(interval(-np.pi / 4, np.pi / 4))
|
| 112 |
+
assert a.start == np.sin(-np.pi / 4)
|
| 113 |
+
assert a.end == np.sin(np.pi / 4)
|
| 114 |
+
|
| 115 |
+
a = sin(interval(np.pi / 4, 3 * np.pi / 4))
|
| 116 |
+
assert a.start == np.sin(np.pi / 4)
|
| 117 |
+
assert a.end == 1
|
| 118 |
+
|
| 119 |
+
a = sin(interval(7 * np.pi / 6, 7 * np.pi / 4))
|
| 120 |
+
assert a.start == -1
|
| 121 |
+
assert a.end == np.sin(7 * np.pi / 6)
|
| 122 |
+
|
| 123 |
+
a = sin(interval(0, 3 * np.pi))
|
| 124 |
+
assert a.start == -1
|
| 125 |
+
assert a.end == 1
|
| 126 |
+
|
| 127 |
+
a = sin(interval(np.pi / 3, 7 * np.pi / 4))
|
| 128 |
+
assert a.start == -1
|
| 129 |
+
assert a.end == 1
|
| 130 |
+
|
| 131 |
+
a = sin(np.pi / 4)
|
| 132 |
+
assert a.start == np.sin(np.pi / 4)
|
| 133 |
+
assert a.end == np.sin(np.pi / 4)
|
| 134 |
+
|
| 135 |
+
a = sin(interval(1, 2, is_valid=False))
|
| 136 |
+
assert a.is_valid is False
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_cos():
|
| 140 |
+
a = cos(interval(0, np.pi / 4))
|
| 141 |
+
assert a.start == np.cos(np.pi / 4)
|
| 142 |
+
assert a.end == 1
|
| 143 |
+
|
| 144 |
+
a = cos(interval(-np.pi / 4, np.pi / 4))
|
| 145 |
+
assert a.start == np.cos(-np.pi / 4)
|
| 146 |
+
assert a.end == 1
|
| 147 |
+
|
| 148 |
+
a = cos(interval(np.pi / 4, 3 * np.pi / 4))
|
| 149 |
+
assert a.start == np.cos(3 * np.pi / 4)
|
| 150 |
+
assert a.end == np.cos(np.pi / 4)
|
| 151 |
+
|
| 152 |
+
a = cos(interval(3 * np.pi / 4, 5 * np.pi / 4))
|
| 153 |
+
assert a.start == -1
|
| 154 |
+
assert a.end == np.cos(3 * np.pi / 4)
|
| 155 |
+
|
| 156 |
+
a = cos(interval(0, 3 * np.pi))
|
| 157 |
+
assert a.start == -1
|
| 158 |
+
assert a.end == 1
|
| 159 |
+
|
| 160 |
+
a = cos(interval(- np.pi / 3, 5 * np.pi / 4))
|
| 161 |
+
assert a.start == -1
|
| 162 |
+
assert a.end == 1
|
| 163 |
+
|
| 164 |
+
a = cos(interval(1, 2, is_valid=False))
|
| 165 |
+
assert a.is_valid is False
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def test_tan():
|
| 169 |
+
a = tan(interval(0, np.pi / 4))
|
| 170 |
+
assert a.start == 0
|
| 171 |
+
# must match lib_interval definition of tan:
|
| 172 |
+
assert a.end == np.sin(np.pi / 4)/np.cos(np.pi / 4)
|
| 173 |
+
|
| 174 |
+
a = tan(interval(np.pi / 4, 3 * np.pi / 4))
|
| 175 |
+
#discontinuity
|
| 176 |
+
assert a.is_valid is None
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def test_sqrt():
|
| 180 |
+
a = sqrt(interval(1, 4))
|
| 181 |
+
assert a.start == 1
|
| 182 |
+
assert a.end == 2
|
| 183 |
+
|
| 184 |
+
a = sqrt(interval(0.01, 1))
|
| 185 |
+
assert a.start == np.sqrt(0.01)
|
| 186 |
+
assert a.end == 1
|
| 187 |
+
|
| 188 |
+
a = sqrt(interval(-1, 1))
|
| 189 |
+
assert a.is_valid is None
|
| 190 |
+
|
| 191 |
+
a = sqrt(interval(-3, -1))
|
| 192 |
+
assert a.is_valid is False
|
| 193 |
+
|
| 194 |
+
a = sqrt(4)
|
| 195 |
+
assert (a == interval(2, 2)) == (True, True)
|
| 196 |
+
|
| 197 |
+
a = sqrt(-3)
|
| 198 |
+
assert a.is_valid is False
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def test_imin():
|
| 202 |
+
a = imin(interval(1, 3), interval(2, 5), interval(-1, 3))
|
| 203 |
+
assert a.start == -1
|
| 204 |
+
assert a.end == 3
|
| 205 |
+
|
| 206 |
+
a = imin(-2, interval(1, 4))
|
| 207 |
+
assert a.start == -2
|
| 208 |
+
assert a.end == -2
|
| 209 |
+
|
| 210 |
+
a = imin(5, interval(3, 4), interval(-2, 2, is_valid=False))
|
| 211 |
+
assert a.start == 3
|
| 212 |
+
assert a.end == 4
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def test_imax():
|
| 216 |
+
a = imax(interval(-2, 2), interval(2, 7), interval(-3, 9))
|
| 217 |
+
assert a.start == 2
|
| 218 |
+
assert a.end == 9
|
| 219 |
+
|
| 220 |
+
a = imax(8, interval(1, 4))
|
| 221 |
+
assert a.start == 8
|
| 222 |
+
assert a.end == 8
|
| 223 |
+
|
| 224 |
+
a = imax(interval(1, 2), interval(3, 4), interval(-2, 2, is_valid=False))
|
| 225 |
+
assert a.start == 3
|
| 226 |
+
assert a.end == 4
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def test_sinh():
|
| 230 |
+
a = sinh(interval(-1, 1))
|
| 231 |
+
assert a.start == np.sinh(-1)
|
| 232 |
+
assert a.end == np.sinh(1)
|
| 233 |
+
|
| 234 |
+
a = sinh(1)
|
| 235 |
+
assert a.start == np.sinh(1)
|
| 236 |
+
assert a.end == np.sinh(1)
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
def test_cosh():
|
| 240 |
+
a = cosh(interval(1, 2))
|
| 241 |
+
assert a.start == np.cosh(1)
|
| 242 |
+
assert a.end == np.cosh(2)
|
| 243 |
+
a = cosh(interval(-2, -1))
|
| 244 |
+
assert a.start == np.cosh(-1)
|
| 245 |
+
assert a.end == np.cosh(-2)
|
| 246 |
+
|
| 247 |
+
a = cosh(interval(-2, 1))
|
| 248 |
+
assert a.start == 1
|
| 249 |
+
assert a.end == np.cosh(-2)
|
| 250 |
+
|
| 251 |
+
a = cosh(1)
|
| 252 |
+
assert a.start == np.cosh(1)
|
| 253 |
+
assert a.end == np.cosh(1)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def test_tanh():
|
| 257 |
+
a = tanh(interval(-3, 3))
|
| 258 |
+
assert a.start == np.tanh(-3)
|
| 259 |
+
assert a.end == np.tanh(3)
|
| 260 |
+
|
| 261 |
+
a = tanh(3)
|
| 262 |
+
assert a.start == np.tanh(3)
|
| 263 |
+
assert a.end == np.tanh(3)
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
def test_asin():
|
| 267 |
+
a = asin(interval(-0.5, 0.5))
|
| 268 |
+
assert a.start == np.arcsin(-0.5)
|
| 269 |
+
assert a.end == np.arcsin(0.5)
|
| 270 |
+
|
| 271 |
+
a = asin(interval(-1.5, 1.5))
|
| 272 |
+
assert a.is_valid is None
|
| 273 |
+
a = asin(interval(-2, -1.5))
|
| 274 |
+
assert a.is_valid is False
|
| 275 |
+
|
| 276 |
+
a = asin(interval(0, 2))
|
| 277 |
+
assert a.is_valid is None
|
| 278 |
+
|
| 279 |
+
a = asin(interval(2, 5))
|
| 280 |
+
assert a.is_valid is False
|
| 281 |
+
|
| 282 |
+
a = asin(0.5)
|
| 283 |
+
assert a.start == np.arcsin(0.5)
|
| 284 |
+
assert a.end == np.arcsin(0.5)
|
| 285 |
+
|
| 286 |
+
a = asin(1.5)
|
| 287 |
+
assert a.is_valid is False
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def test_acos():
|
| 291 |
+
a = acos(interval(-0.5, 0.5))
|
| 292 |
+
assert a.start == np.arccos(0.5)
|
| 293 |
+
assert a.end == np.arccos(-0.5)
|
| 294 |
+
|
| 295 |
+
a = acos(interval(-1.5, 1.5))
|
| 296 |
+
assert a.is_valid is None
|
| 297 |
+
a = acos(interval(-2, -1.5))
|
| 298 |
+
assert a.is_valid is False
|
| 299 |
+
|
| 300 |
+
a = acos(interval(0, 2))
|
| 301 |
+
assert a.is_valid is None
|
| 302 |
+
|
| 303 |
+
a = acos(interval(2, 5))
|
| 304 |
+
assert a.is_valid is False
|
| 305 |
+
|
| 306 |
+
a = acos(0.5)
|
| 307 |
+
assert a.start == np.arccos(0.5)
|
| 308 |
+
assert a.end == np.arccos(0.5)
|
| 309 |
+
|
| 310 |
+
a = acos(1.5)
|
| 311 |
+
assert a.is_valid is False
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def test_ceil():
|
| 315 |
+
a = ceil(interval(0.2, 0.5))
|
| 316 |
+
assert a.start == 1
|
| 317 |
+
assert a.end == 1
|
| 318 |
+
|
| 319 |
+
a = ceil(interval(0.5, 1.5))
|
| 320 |
+
assert a.start == 1
|
| 321 |
+
assert a.end == 2
|
| 322 |
+
assert a.is_valid is None
|
| 323 |
+
|
| 324 |
+
a = ceil(interval(-5, 5))
|
| 325 |
+
assert a.is_valid is None
|
| 326 |
+
|
| 327 |
+
a = ceil(5.4)
|
| 328 |
+
assert a.start == 6
|
| 329 |
+
assert a.end == 6
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
def test_floor():
|
| 333 |
+
a = floor(interval(0.2, 0.5))
|
| 334 |
+
assert a.start == 0
|
| 335 |
+
assert a.end == 0
|
| 336 |
+
|
| 337 |
+
a = floor(interval(0.5, 1.5))
|
| 338 |
+
assert a.start == 0
|
| 339 |
+
assert a.end == 1
|
| 340 |
+
assert a.is_valid is None
|
| 341 |
+
|
| 342 |
+
a = floor(interval(-5, 5))
|
| 343 |
+
assert a.is_valid is None
|
| 344 |
+
|
| 345 |
+
a = floor(5.4)
|
| 346 |
+
assert a.start == 5
|
| 347 |
+
assert a.end == 5
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def test_asinh():
|
| 351 |
+
a = asinh(interval(1, 2))
|
| 352 |
+
assert a.start == np.arcsinh(1)
|
| 353 |
+
assert a.end == np.arcsinh(2)
|
| 354 |
+
|
| 355 |
+
a = asinh(0.5)
|
| 356 |
+
assert a.start == np.arcsinh(0.5)
|
| 357 |
+
assert a.end == np.arcsinh(0.5)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def test_acosh():
|
| 361 |
+
a = acosh(interval(3, 5))
|
| 362 |
+
assert a.start == np.arccosh(3)
|
| 363 |
+
assert a.end == np.arccosh(5)
|
| 364 |
+
|
| 365 |
+
a = acosh(interval(0, 3))
|
| 366 |
+
assert a.is_valid is None
|
| 367 |
+
a = acosh(interval(-3, 0.5))
|
| 368 |
+
assert a.is_valid is False
|
| 369 |
+
|
| 370 |
+
a = acosh(0.5)
|
| 371 |
+
assert a.is_valid is False
|
| 372 |
+
|
| 373 |
+
a = acosh(2)
|
| 374 |
+
assert a.start == np.arccosh(2)
|
| 375 |
+
assert a.end == np.arccosh(2)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def test_atanh():
|
| 379 |
+
a = atanh(interval(-0.5, 0.5))
|
| 380 |
+
assert a.start == np.arctanh(-0.5)
|
| 381 |
+
assert a.end == np.arctanh(0.5)
|
| 382 |
+
|
| 383 |
+
a = atanh(interval(0, 3))
|
| 384 |
+
assert a.is_valid is None
|
| 385 |
+
|
| 386 |
+
a = atanh(interval(-3, -2))
|
| 387 |
+
assert a.is_valid is False
|
| 388 |
+
|
| 389 |
+
a = atanh(0.5)
|
| 390 |
+
assert a.start == np.arctanh(0.5)
|
| 391 |
+
assert a.end == np.arctanh(0.5)
|
| 392 |
+
|
| 393 |
+
a = atanh(1.5)
|
| 394 |
+
assert a.is_valid is False
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def test_Abs():
|
| 398 |
+
assert (Abs(interval(-0.5, 0.5)) == interval(0, 0.5)) == (True, True)
|
| 399 |
+
assert (Abs(interval(-3, -2)) == interval(2, 3)) == (True, True)
|
| 400 |
+
assert (Abs(-3) == interval(3, 3)) == (True, True)
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def test_And():
|
| 404 |
+
args = [(True, True), (True, False), (True, None)]
|
| 405 |
+
assert And(*args) == (True, False)
|
| 406 |
+
|
| 407 |
+
args = [(False, True), (None, None), (True, True)]
|
| 408 |
+
assert And(*args) == (False, None)
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def test_Or():
|
| 412 |
+
args = [(True, True), (True, False), (False, None)]
|
| 413 |
+
assert Or(*args) == (True, True)
|
| 414 |
+
args = [(None, None), (False, None), (False, False)]
|
| 415 |
+
assert Or(*args) == (None, None)
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/test_interval_membership.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.symbol import Symbol
|
| 2 |
+
from sympy.plotting.intervalmath import interval
|
| 3 |
+
from sympy.plotting.intervalmath.interval_membership import intervalMembership
|
| 4 |
+
from sympy.plotting.experimental_lambdify import experimental_lambdify
|
| 5 |
+
from sympy.testing.pytest import raises
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_creation():
|
| 9 |
+
assert intervalMembership(True, True)
|
| 10 |
+
raises(TypeError, lambda: intervalMembership(True))
|
| 11 |
+
raises(TypeError, lambda: intervalMembership(True, True, True))
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_getitem():
|
| 15 |
+
a = intervalMembership(True, False)
|
| 16 |
+
assert a[0] is True
|
| 17 |
+
assert a[1] is False
|
| 18 |
+
raises(IndexError, lambda: a[2])
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_str():
|
| 22 |
+
a = intervalMembership(True, False)
|
| 23 |
+
assert str(a) == 'intervalMembership(True, False)'
|
| 24 |
+
assert repr(a) == 'intervalMembership(True, False)'
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def test_equivalence():
|
| 28 |
+
a = intervalMembership(True, True)
|
| 29 |
+
b = intervalMembership(True, False)
|
| 30 |
+
assert (a == b) is False
|
| 31 |
+
assert (a != b) is True
|
| 32 |
+
|
| 33 |
+
a = intervalMembership(True, False)
|
| 34 |
+
b = intervalMembership(True, False)
|
| 35 |
+
assert (a == b) is True
|
| 36 |
+
assert (a != b) is False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_not():
|
| 40 |
+
x = Symbol('x')
|
| 41 |
+
|
| 42 |
+
r1 = x > -1
|
| 43 |
+
r2 = x <= -1
|
| 44 |
+
|
| 45 |
+
i = interval
|
| 46 |
+
|
| 47 |
+
f1 = experimental_lambdify((x,), r1)
|
| 48 |
+
f2 = experimental_lambdify((x,), r2)
|
| 49 |
+
|
| 50 |
+
tt = i(-0.1, 0.1, is_valid=True)
|
| 51 |
+
tn = i(-0.1, 0.1, is_valid=None)
|
| 52 |
+
tf = i(-0.1, 0.1, is_valid=False)
|
| 53 |
+
|
| 54 |
+
assert f1(tt) == ~f2(tt)
|
| 55 |
+
assert f1(tn) == ~f2(tn)
|
| 56 |
+
assert f1(tf) == ~f2(tf)
|
| 57 |
+
|
| 58 |
+
nt = i(0.9, 1.1, is_valid=True)
|
| 59 |
+
nn = i(0.9, 1.1, is_valid=None)
|
| 60 |
+
nf = i(0.9, 1.1, is_valid=False)
|
| 61 |
+
|
| 62 |
+
assert f1(nt) == ~f2(nt)
|
| 63 |
+
assert f1(nn) == ~f2(nn)
|
| 64 |
+
assert f1(nf) == ~f2(nf)
|
| 65 |
+
|
| 66 |
+
ft = i(1.9, 2.1, is_valid=True)
|
| 67 |
+
fn = i(1.9, 2.1, is_valid=None)
|
| 68 |
+
ff = i(1.9, 2.1, is_valid=False)
|
| 69 |
+
|
| 70 |
+
assert f1(ft) == ~f2(ft)
|
| 71 |
+
assert f1(fn) == ~f2(fn)
|
| 72 |
+
assert f1(ff) == ~f2(ff)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_boolean():
|
| 76 |
+
# There can be 9*9 test cases in full mapping of the cartesian product.
|
| 77 |
+
# But we only consider 3*3 cases for simplicity.
|
| 78 |
+
s = [
|
| 79 |
+
intervalMembership(False, False),
|
| 80 |
+
intervalMembership(None, None),
|
| 81 |
+
intervalMembership(True, True)
|
| 82 |
+
]
|
| 83 |
+
|
| 84 |
+
# Reduced tests for 'And'
|
| 85 |
+
a1 = [
|
| 86 |
+
intervalMembership(False, False),
|
| 87 |
+
intervalMembership(False, False),
|
| 88 |
+
intervalMembership(False, False),
|
| 89 |
+
intervalMembership(False, False),
|
| 90 |
+
intervalMembership(None, None),
|
| 91 |
+
intervalMembership(None, None),
|
| 92 |
+
intervalMembership(False, False),
|
| 93 |
+
intervalMembership(None, None),
|
| 94 |
+
intervalMembership(True, True)
|
| 95 |
+
]
|
| 96 |
+
a1_iter = iter(a1)
|
| 97 |
+
for i in range(len(s)):
|
| 98 |
+
for j in range(len(s)):
|
| 99 |
+
assert s[i] & s[j] == next(a1_iter)
|
| 100 |
+
|
| 101 |
+
# Reduced tests for 'Or'
|
| 102 |
+
a1 = [
|
| 103 |
+
intervalMembership(False, False),
|
| 104 |
+
intervalMembership(None, False),
|
| 105 |
+
intervalMembership(True, False),
|
| 106 |
+
intervalMembership(None, False),
|
| 107 |
+
intervalMembership(None, None),
|
| 108 |
+
intervalMembership(True, None),
|
| 109 |
+
intervalMembership(True, False),
|
| 110 |
+
intervalMembership(True, None),
|
| 111 |
+
intervalMembership(True, True)
|
| 112 |
+
]
|
| 113 |
+
a1_iter = iter(a1)
|
| 114 |
+
for i in range(len(s)):
|
| 115 |
+
for j in range(len(s)):
|
| 116 |
+
assert s[i] | s[j] == next(a1_iter)
|
| 117 |
+
|
| 118 |
+
# Reduced tests for 'Xor'
|
| 119 |
+
a1 = [
|
| 120 |
+
intervalMembership(False, False),
|
| 121 |
+
intervalMembership(None, False),
|
| 122 |
+
intervalMembership(True, False),
|
| 123 |
+
intervalMembership(None, False),
|
| 124 |
+
intervalMembership(None, None),
|
| 125 |
+
intervalMembership(None, None),
|
| 126 |
+
intervalMembership(True, False),
|
| 127 |
+
intervalMembership(None, None),
|
| 128 |
+
intervalMembership(False, True)
|
| 129 |
+
]
|
| 130 |
+
a1_iter = iter(a1)
|
| 131 |
+
for i in range(len(s)):
|
| 132 |
+
for j in range(len(s)):
|
| 133 |
+
assert s[i] ^ s[j] == next(a1_iter)
|
| 134 |
+
|
| 135 |
+
# Reduced tests for 'Not'
|
| 136 |
+
a1 = [
|
| 137 |
+
intervalMembership(True, False),
|
| 138 |
+
intervalMembership(None, None),
|
| 139 |
+
intervalMembership(False, True)
|
| 140 |
+
]
|
| 141 |
+
a1_iter = iter(a1)
|
| 142 |
+
for i in range(len(s)):
|
| 143 |
+
assert ~s[i] == next(a1_iter)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def test_boolean_errors():
|
| 147 |
+
a = intervalMembership(True, True)
|
| 148 |
+
raises(ValueError, lambda: a & 1)
|
| 149 |
+
raises(ValueError, lambda: a | 1)
|
| 150 |
+
raises(ValueError, lambda: a ^ 1)
|
lib/python3.12/site-packages/sympy/plotting/intervalmath/tests/test_intervalmath.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.intervalmath import interval
|
| 2 |
+
from sympy.testing.pytest import raises
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def test_interval():
|
| 6 |
+
assert (interval(1, 1) == interval(1, 1, is_valid=True)) == (True, True)
|
| 7 |
+
assert (interval(1, 1) == interval(1, 1, is_valid=False)) == (True, False)
|
| 8 |
+
assert (interval(1, 1) == interval(1, 1, is_valid=None)) == (True, None)
|
| 9 |
+
assert (interval(1, 1.5) == interval(1, 2)) == (None, True)
|
| 10 |
+
assert (interval(0, 1) == interval(2, 3)) == (False, True)
|
| 11 |
+
assert (interval(0, 1) == interval(1, 2)) == (None, True)
|
| 12 |
+
assert (interval(1, 2) != interval(1, 2)) == (False, True)
|
| 13 |
+
assert (interval(1, 3) != interval(2, 3)) == (None, True)
|
| 14 |
+
assert (interval(1, 3) != interval(-5, -3)) == (True, True)
|
| 15 |
+
assert (
|
| 16 |
+
interval(1, 3, is_valid=False) != interval(-5, -3)) == (True, False)
|
| 17 |
+
assert (interval(1, 3, is_valid=None) != interval(-5, 3)) == (None, None)
|
| 18 |
+
assert (interval(4, 4) != 4) == (False, True)
|
| 19 |
+
assert (interval(1, 1) == 1) == (True, True)
|
| 20 |
+
assert (interval(1, 3, is_valid=False) == interval(1, 3)) == (True, False)
|
| 21 |
+
assert (interval(1, 3, is_valid=None) == interval(1, 3)) == (True, None)
|
| 22 |
+
inter = interval(-5, 5)
|
| 23 |
+
assert (interval(inter) == interval(-5, 5)) == (True, True)
|
| 24 |
+
assert inter.width == 10
|
| 25 |
+
assert 0 in inter
|
| 26 |
+
assert -5 in inter
|
| 27 |
+
assert 5 in inter
|
| 28 |
+
assert interval(0, 3) in inter
|
| 29 |
+
assert interval(-6, 2) not in inter
|
| 30 |
+
assert -5.05 not in inter
|
| 31 |
+
assert 5.3 not in inter
|
| 32 |
+
interb = interval(-float('inf'), float('inf'))
|
| 33 |
+
assert 0 in inter
|
| 34 |
+
assert inter in interb
|
| 35 |
+
assert interval(0, float('inf')) in interb
|
| 36 |
+
assert interval(-float('inf'), 5) in interb
|
| 37 |
+
assert interval(-1e50, 1e50) in interb
|
| 38 |
+
assert (
|
| 39 |
+
-interval(-1, -2, is_valid=False) == interval(1, 2)) == (True, False)
|
| 40 |
+
raises(ValueError, lambda: interval(1, 2, 3))
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_interval_add():
|
| 44 |
+
assert (interval(1, 2) + interval(2, 3) == interval(3, 5)) == (True, True)
|
| 45 |
+
assert (1 + interval(1, 2) == interval(2, 3)) == (True, True)
|
| 46 |
+
assert (interval(1, 2) + 1 == interval(2, 3)) == (True, True)
|
| 47 |
+
compare = (1 + interval(0, float('inf')) == interval(1, float('inf')))
|
| 48 |
+
assert compare == (True, True)
|
| 49 |
+
a = 1 + interval(2, 5, is_valid=False)
|
| 50 |
+
assert a.is_valid is False
|
| 51 |
+
a = 1 + interval(2, 5, is_valid=None)
|
| 52 |
+
assert a.is_valid is None
|
| 53 |
+
a = interval(2, 5, is_valid=False) + interval(3, 5, is_valid=None)
|
| 54 |
+
assert a.is_valid is False
|
| 55 |
+
a = interval(3, 5) + interval(-1, 1, is_valid=None)
|
| 56 |
+
assert a.is_valid is None
|
| 57 |
+
a = interval(2, 5, is_valid=False) + 1
|
| 58 |
+
assert a.is_valid is False
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def test_interval_sub():
|
| 62 |
+
assert (interval(1, 2) - interval(1, 5) == interval(-4, 1)) == (True, True)
|
| 63 |
+
assert (interval(1, 2) - 1 == interval(0, 1)) == (True, True)
|
| 64 |
+
assert (1 - interval(1, 2) == interval(-1, 0)) == (True, True)
|
| 65 |
+
a = 1 - interval(1, 2, is_valid=False)
|
| 66 |
+
assert a.is_valid is False
|
| 67 |
+
a = interval(1, 4, is_valid=None) - 1
|
| 68 |
+
assert a.is_valid is None
|
| 69 |
+
a = interval(1, 3, is_valid=False) - interval(1, 3)
|
| 70 |
+
assert a.is_valid is False
|
| 71 |
+
a = interval(1, 3, is_valid=None) - interval(1, 3)
|
| 72 |
+
assert a.is_valid is None
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_interval_inequality():
|
| 76 |
+
assert (interval(1, 2) < interval(3, 4)) == (True, True)
|
| 77 |
+
assert (interval(1, 2) < interval(2, 4)) == (None, True)
|
| 78 |
+
assert (interval(1, 2) < interval(-2, 0)) == (False, True)
|
| 79 |
+
assert (interval(1, 2) <= interval(2, 4)) == (True, True)
|
| 80 |
+
assert (interval(1, 2) <= interval(1.5, 6)) == (None, True)
|
| 81 |
+
assert (interval(2, 3) <= interval(1, 2)) == (None, True)
|
| 82 |
+
assert (interval(2, 3) <= interval(1, 1.5)) == (False, True)
|
| 83 |
+
assert (
|
| 84 |
+
interval(1, 2, is_valid=False) <= interval(-2, 0)) == (False, False)
|
| 85 |
+
assert (interval(1, 2, is_valid=None) <= interval(-2, 0)) == (False, None)
|
| 86 |
+
assert (interval(1, 2) <= 1.5) == (None, True)
|
| 87 |
+
assert (interval(1, 2) <= 3) == (True, True)
|
| 88 |
+
assert (interval(1, 2) <= 0) == (False, True)
|
| 89 |
+
assert (interval(5, 8) > interval(2, 3)) == (True, True)
|
| 90 |
+
assert (interval(2, 5) > interval(1, 3)) == (None, True)
|
| 91 |
+
assert (interval(2, 3) > interval(3.1, 5)) == (False, True)
|
| 92 |
+
|
| 93 |
+
assert (interval(-1, 1) == 0) == (None, True)
|
| 94 |
+
assert (interval(-1, 1) == 2) == (False, True)
|
| 95 |
+
assert (interval(-1, 1) != 0) == (None, True)
|
| 96 |
+
assert (interval(-1, 1) != 2) == (True, True)
|
| 97 |
+
|
| 98 |
+
assert (interval(3, 5) > 2) == (True, True)
|
| 99 |
+
assert (interval(3, 5) < 2) == (False, True)
|
| 100 |
+
assert (interval(1, 5) < 2) == (None, True)
|
| 101 |
+
assert (interval(1, 5) > 2) == (None, True)
|
| 102 |
+
assert (interval(0, 1) > 2) == (False, True)
|
| 103 |
+
assert (interval(1, 2) >= interval(0, 1)) == (True, True)
|
| 104 |
+
assert (interval(1, 2) >= interval(0, 1.5)) == (None, True)
|
| 105 |
+
assert (interval(1, 2) >= interval(3, 4)) == (False, True)
|
| 106 |
+
assert (interval(1, 2) >= 0) == (True, True)
|
| 107 |
+
assert (interval(1, 2) >= 1.2) == (None, True)
|
| 108 |
+
assert (interval(1, 2) >= 3) == (False, True)
|
| 109 |
+
assert (2 > interval(0, 1)) == (True, True)
|
| 110 |
+
a = interval(-1, 1, is_valid=False) < interval(2, 5, is_valid=None)
|
| 111 |
+
assert a == (True, False)
|
| 112 |
+
a = interval(-1, 1, is_valid=None) < interval(2, 5, is_valid=False)
|
| 113 |
+
assert a == (True, False)
|
| 114 |
+
a = interval(-1, 1, is_valid=None) < interval(2, 5, is_valid=None)
|
| 115 |
+
assert a == (True, None)
|
| 116 |
+
a = interval(-1, 1, is_valid=False) > interval(-5, -2, is_valid=None)
|
| 117 |
+
assert a == (True, False)
|
| 118 |
+
a = interval(-1, 1, is_valid=None) > interval(-5, -2, is_valid=False)
|
| 119 |
+
assert a == (True, False)
|
| 120 |
+
a = interval(-1, 1, is_valid=None) > interval(-5, -2, is_valid=None)
|
| 121 |
+
assert a == (True, None)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def test_interval_mul():
|
| 125 |
+
assert (
|
| 126 |
+
interval(1, 5) * interval(2, 10) == interval(2, 50)) == (True, True)
|
| 127 |
+
a = interval(-1, 1) * interval(2, 10) == interval(-10, 10)
|
| 128 |
+
assert a == (True, True)
|
| 129 |
+
|
| 130 |
+
a = interval(-1, 1) * interval(-5, 3) == interval(-5, 5)
|
| 131 |
+
assert a == (True, True)
|
| 132 |
+
|
| 133 |
+
assert (interval(1, 3) * 2 == interval(2, 6)) == (True, True)
|
| 134 |
+
assert (3 * interval(-1, 2) == interval(-3, 6)) == (True, True)
|
| 135 |
+
|
| 136 |
+
a = 3 * interval(1, 2, is_valid=False)
|
| 137 |
+
assert a.is_valid is False
|
| 138 |
+
|
| 139 |
+
a = 3 * interval(1, 2, is_valid=None)
|
| 140 |
+
assert a.is_valid is None
|
| 141 |
+
|
| 142 |
+
a = interval(1, 5, is_valid=False) * interval(1, 2, is_valid=None)
|
| 143 |
+
assert a.is_valid is False
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def test_interval_div():
|
| 147 |
+
div = interval(1, 2, is_valid=False) / 3
|
| 148 |
+
assert div == interval(-float('inf'), float('inf'), is_valid=False)
|
| 149 |
+
|
| 150 |
+
div = interval(1, 2, is_valid=None) / 3
|
| 151 |
+
assert div == interval(-float('inf'), float('inf'), is_valid=None)
|
| 152 |
+
|
| 153 |
+
div = 3 / interval(1, 2, is_valid=None)
|
| 154 |
+
assert div == interval(-float('inf'), float('inf'), is_valid=None)
|
| 155 |
+
a = interval(1, 2) / 0
|
| 156 |
+
assert a.is_valid is False
|
| 157 |
+
a = interval(0.5, 1) / interval(-1, 0)
|
| 158 |
+
assert a.is_valid is None
|
| 159 |
+
a = interval(0, 1) / interval(0, 1)
|
| 160 |
+
assert a.is_valid is None
|
| 161 |
+
|
| 162 |
+
a = interval(-1, 1) / interval(-1, 1)
|
| 163 |
+
assert a.is_valid is None
|
| 164 |
+
|
| 165 |
+
a = interval(-1, 2) / interval(0.5, 1) == interval(-2.0, 4.0)
|
| 166 |
+
assert a == (True, True)
|
| 167 |
+
a = interval(0, 1) / interval(0.5, 1) == interval(0.0, 2.0)
|
| 168 |
+
assert a == (True, True)
|
| 169 |
+
a = interval(-1, 0) / interval(0.5, 1) == interval(-2.0, 0.0)
|
| 170 |
+
assert a == (True, True)
|
| 171 |
+
a = interval(-0.5, -0.25) / interval(0.5, 1) == interval(-1.0, -0.25)
|
| 172 |
+
assert a == (True, True)
|
| 173 |
+
a = interval(0.5, 1) / interval(0.5, 1) == interval(0.5, 2.0)
|
| 174 |
+
assert a == (True, True)
|
| 175 |
+
a = interval(0.5, 4) / interval(0.5, 1) == interval(0.5, 8.0)
|
| 176 |
+
assert a == (True, True)
|
| 177 |
+
a = interval(-1, -0.5) / interval(0.5, 1) == interval(-2.0, -0.5)
|
| 178 |
+
assert a == (True, True)
|
| 179 |
+
a = interval(-4, -0.5) / interval(0.5, 1) == interval(-8.0, -0.5)
|
| 180 |
+
assert a == (True, True)
|
| 181 |
+
a = interval(-1, 2) / interval(-2, -0.5) == interval(-4.0, 2.0)
|
| 182 |
+
assert a == (True, True)
|
| 183 |
+
a = interval(0, 1) / interval(-2, -0.5) == interval(-2.0, 0.0)
|
| 184 |
+
assert a == (True, True)
|
| 185 |
+
a = interval(-1, 0) / interval(-2, -0.5) == interval(0.0, 2.0)
|
| 186 |
+
assert a == (True, True)
|
| 187 |
+
a = interval(-0.5, -0.25) / interval(-2, -0.5) == interval(0.125, 1.0)
|
| 188 |
+
assert a == (True, True)
|
| 189 |
+
a = interval(0.5, 1) / interval(-2, -0.5) == interval(-2.0, -0.25)
|
| 190 |
+
assert a == (True, True)
|
| 191 |
+
a = interval(0.5, 4) / interval(-2, -0.5) == interval(-8.0, -0.25)
|
| 192 |
+
assert a == (True, True)
|
| 193 |
+
a = interval(-1, -0.5) / interval(-2, -0.5) == interval(0.25, 2.0)
|
| 194 |
+
assert a == (True, True)
|
| 195 |
+
a = interval(-4, -0.5) / interval(-2, -0.5) == interval(0.25, 8.0)
|
| 196 |
+
assert a == (True, True)
|
| 197 |
+
a = interval(-5, 5, is_valid=False) / 2
|
| 198 |
+
assert a.is_valid is False
|
| 199 |
+
|
| 200 |
+
def test_hashable():
|
| 201 |
+
'''
|
| 202 |
+
test that interval objects are hashable.
|
| 203 |
+
this is required in order to be able to put them into the cache, which
|
| 204 |
+
appears to be necessary for plotting in py3k. For details, see:
|
| 205 |
+
|
| 206 |
+
https://github.com/sympy/sympy/pull/2101
|
| 207 |
+
https://github.com/sympy/sympy/issues/6533
|
| 208 |
+
'''
|
| 209 |
+
hash(interval(1, 1))
|
| 210 |
+
hash(interval(1, 1, is_valid=True))
|
| 211 |
+
hash(interval(-4, -0.5))
|
| 212 |
+
hash(interval(-2, -0.5))
|
| 213 |
+
hash(interval(0.25, 8.0))
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (4.06 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/managed_window.cpython-312.pyc
ADDED
|
Binary file (4.63 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_curve.cpython-312.pyc
ADDED
|
Binary file (5.04 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_mode.cpython-312.pyc
ADDED
|
Binary file (14.7 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_mode_base.cpython-312.pyc
ADDED
|
Binary file (17 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_object.cpython-312.pyc
ADDED
|
Binary file (895 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_rotation.cpython-312.pyc
ADDED
|
Binary file (3.28 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/plot_surface.cpython-312.pyc
ADDED
|
Binary file (6.07 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/plotting/pygletplot/__pycache__/util.cpython-312.pyc
ADDED
|
Binary file (9.73 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/printing/codeprinter.py
ADDED
|
@@ -0,0 +1,888 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
from typing import Any
|
| 3 |
+
|
| 4 |
+
from functools import wraps
|
| 5 |
+
|
| 6 |
+
from sympy.core import Add, Mul, Pow, S, sympify, Float
|
| 7 |
+
from sympy.core.basic import Basic
|
| 8 |
+
from sympy.core.expr import UnevaluatedExpr
|
| 9 |
+
from sympy.core.function import Lambda
|
| 10 |
+
from sympy.core.mul import _keep_coeff
|
| 11 |
+
from sympy.core.sorting import default_sort_key
|
| 12 |
+
from sympy.core.symbol import Symbol
|
| 13 |
+
from sympy.functions.elementary.complexes import re
|
| 14 |
+
from sympy.printing.str import StrPrinter
|
| 15 |
+
from sympy.printing.precedence import precedence, PRECEDENCE
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class requires:
|
| 19 |
+
""" Decorator for registering requirements on print methods. """
|
| 20 |
+
def __init__(self, **kwargs):
|
| 21 |
+
self._req = kwargs
|
| 22 |
+
|
| 23 |
+
def __call__(self, method):
|
| 24 |
+
def _method_wrapper(self_, *args, **kwargs):
|
| 25 |
+
for k, v in self._req.items():
|
| 26 |
+
getattr(self_, k).update(v)
|
| 27 |
+
return method(self_, *args, **kwargs)
|
| 28 |
+
return wraps(method)(_method_wrapper)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class AssignmentError(Exception):
|
| 32 |
+
"""
|
| 33 |
+
Raised if an assignment variable for a loop is missing.
|
| 34 |
+
"""
|
| 35 |
+
pass
|
| 36 |
+
|
| 37 |
+
class PrintMethodNotImplementedError(NotImplementedError):
|
| 38 |
+
"""
|
| 39 |
+
Raised if a _print_* method is missing in the Printer.
|
| 40 |
+
"""
|
| 41 |
+
pass
|
| 42 |
+
|
| 43 |
+
def _convert_python_lists(arg):
|
| 44 |
+
if isinstance(arg, list):
|
| 45 |
+
from sympy.codegen.abstract_nodes import List
|
| 46 |
+
return List(*(_convert_python_lists(e) for e in arg))
|
| 47 |
+
elif isinstance(arg, tuple):
|
| 48 |
+
return tuple(_convert_python_lists(e) for e in arg)
|
| 49 |
+
else:
|
| 50 |
+
return arg
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class CodePrinter(StrPrinter):
|
| 54 |
+
"""
|
| 55 |
+
The base class for code-printing subclasses.
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
_operators = {
|
| 59 |
+
'and': '&&',
|
| 60 |
+
'or': '||',
|
| 61 |
+
'not': '!',
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
_default_settings: dict[str, Any] = {
|
| 65 |
+
'order': None,
|
| 66 |
+
'full_prec': 'auto',
|
| 67 |
+
'error_on_reserved': False,
|
| 68 |
+
'reserved_word_suffix': '_',
|
| 69 |
+
'human': True,
|
| 70 |
+
'inline': False,
|
| 71 |
+
'allow_unknown_functions': False,
|
| 72 |
+
'strict': None # True or False; None => True if human == True
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
# Functions which are "simple" to rewrite to other functions that
|
| 76 |
+
# may be supported
|
| 77 |
+
# function_to_rewrite : (function_to_rewrite_to, iterable_with_other_functions_required)
|
| 78 |
+
_rewriteable_functions = {
|
| 79 |
+
'cot': ('tan', []),
|
| 80 |
+
'csc': ('sin', []),
|
| 81 |
+
'sec': ('cos', []),
|
| 82 |
+
'acot': ('atan', []),
|
| 83 |
+
'acsc': ('asin', []),
|
| 84 |
+
'asec': ('acos', []),
|
| 85 |
+
'coth': ('exp', []),
|
| 86 |
+
'csch': ('exp', []),
|
| 87 |
+
'sech': ('exp', []),
|
| 88 |
+
'acoth': ('log', []),
|
| 89 |
+
'acsch': ('log', []),
|
| 90 |
+
'asech': ('log', []),
|
| 91 |
+
'catalan': ('gamma', []),
|
| 92 |
+
'fibonacci': ('sqrt', []),
|
| 93 |
+
'lucas': ('sqrt', []),
|
| 94 |
+
'beta': ('gamma', []),
|
| 95 |
+
'sinc': ('sin', ['Piecewise']),
|
| 96 |
+
'Mod': ('floor', []),
|
| 97 |
+
'factorial': ('gamma', []),
|
| 98 |
+
'factorial2': ('gamma', ['Piecewise']),
|
| 99 |
+
'subfactorial': ('uppergamma', []),
|
| 100 |
+
'RisingFactorial': ('gamma', ['Piecewise']),
|
| 101 |
+
'FallingFactorial': ('gamma', ['Piecewise']),
|
| 102 |
+
'binomial': ('gamma', []),
|
| 103 |
+
'frac': ('floor', []),
|
| 104 |
+
'Max': ('Piecewise', []),
|
| 105 |
+
'Min': ('Piecewise', []),
|
| 106 |
+
'Heaviside': ('Piecewise', []),
|
| 107 |
+
'erf2': ('erf', []),
|
| 108 |
+
'erfc': ('erf', []),
|
| 109 |
+
'Li': ('li', []),
|
| 110 |
+
'Ei': ('li', []),
|
| 111 |
+
'dirichlet_eta': ('zeta', []),
|
| 112 |
+
'riemann_xi': ('zeta', ['gamma']),
|
| 113 |
+
'SingularityFunction': ('Piecewise', []),
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
def __init__(self, settings=None):
|
| 117 |
+
super().__init__(settings=settings)
|
| 118 |
+
if self._settings.get('strict', True) == None:
|
| 119 |
+
# for backwards compatibility, human=False need not to throw:
|
| 120 |
+
self._settings['strict'] = self._settings.get('human', True) == True
|
| 121 |
+
if not hasattr(self, 'reserved_words'):
|
| 122 |
+
self.reserved_words = set()
|
| 123 |
+
|
| 124 |
+
def _handle_UnevaluatedExpr(self, expr):
|
| 125 |
+
return expr.replace(re, lambda arg: arg if isinstance(
|
| 126 |
+
arg, UnevaluatedExpr) and arg.args[0].is_real else re(arg))
|
| 127 |
+
|
| 128 |
+
def doprint(self, expr, assign_to=None):
|
| 129 |
+
"""
|
| 130 |
+
Print the expression as code.
|
| 131 |
+
|
| 132 |
+
Parameters
|
| 133 |
+
----------
|
| 134 |
+
expr : Expression
|
| 135 |
+
The expression to be printed.
|
| 136 |
+
|
| 137 |
+
assign_to : Symbol, string, MatrixSymbol, list of strings or Symbols (optional)
|
| 138 |
+
If provided, the printed code will set the expression to a variable or multiple variables
|
| 139 |
+
with the name or names given in ``assign_to``.
|
| 140 |
+
"""
|
| 141 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 142 |
+
from sympy.codegen.ast import CodeBlock, Assignment
|
| 143 |
+
|
| 144 |
+
def _handle_assign_to(expr, assign_to):
|
| 145 |
+
if assign_to is None:
|
| 146 |
+
return sympify(expr)
|
| 147 |
+
if isinstance(assign_to, (list, tuple)):
|
| 148 |
+
if len(expr) != len(assign_to):
|
| 149 |
+
raise ValueError('Failed to assign an expression of length {} to {} variables'.format(len(expr), len(assign_to)))
|
| 150 |
+
return CodeBlock(*[_handle_assign_to(lhs, rhs) for lhs, rhs in zip(expr, assign_to)])
|
| 151 |
+
if isinstance(assign_to, str):
|
| 152 |
+
if expr.is_Matrix:
|
| 153 |
+
assign_to = MatrixSymbol(assign_to, *expr.shape)
|
| 154 |
+
else:
|
| 155 |
+
assign_to = Symbol(assign_to)
|
| 156 |
+
elif not isinstance(assign_to, Basic):
|
| 157 |
+
raise TypeError("{} cannot assign to object of type {}".format(
|
| 158 |
+
type(self).__name__, type(assign_to)))
|
| 159 |
+
return Assignment(assign_to, expr)
|
| 160 |
+
|
| 161 |
+
expr = _convert_python_lists(expr)
|
| 162 |
+
expr = _handle_assign_to(expr, assign_to)
|
| 163 |
+
|
| 164 |
+
# Remove re(...) nodes due to UnevaluatedExpr.is_real always is None:
|
| 165 |
+
expr = self._handle_UnevaluatedExpr(expr)
|
| 166 |
+
|
| 167 |
+
# keep a set of expressions that are not strictly translatable to Code
|
| 168 |
+
# and number constants that must be declared and initialized
|
| 169 |
+
self._not_supported = set()
|
| 170 |
+
self._number_symbols = set()
|
| 171 |
+
|
| 172 |
+
lines = self._print(expr).splitlines()
|
| 173 |
+
|
| 174 |
+
# format the output
|
| 175 |
+
if self._settings["human"]:
|
| 176 |
+
frontlines = []
|
| 177 |
+
if self._not_supported:
|
| 178 |
+
frontlines.append(self._get_comment(
|
| 179 |
+
"Not supported in {}:".format(self.language)))
|
| 180 |
+
for expr in sorted(self._not_supported, key=str):
|
| 181 |
+
frontlines.append(self._get_comment(type(expr).__name__))
|
| 182 |
+
for name, value in sorted(self._number_symbols, key=str):
|
| 183 |
+
frontlines.append(self._declare_number_const(name, value))
|
| 184 |
+
lines = frontlines + lines
|
| 185 |
+
lines = self._format_code(lines)
|
| 186 |
+
result = "\n".join(lines)
|
| 187 |
+
else:
|
| 188 |
+
lines = self._format_code(lines)
|
| 189 |
+
num_syms = {(k, self._print(v)) for k, v in self._number_symbols}
|
| 190 |
+
result = (num_syms, self._not_supported, "\n".join(lines))
|
| 191 |
+
self._not_supported = set()
|
| 192 |
+
self._number_symbols = set()
|
| 193 |
+
return result
|
| 194 |
+
|
| 195 |
+
def _doprint_loops(self, expr, assign_to=None):
|
| 196 |
+
# Here we print an expression that contains Indexed objects, they
|
| 197 |
+
# correspond to arrays in the generated code. The low-level implementation
|
| 198 |
+
# involves looping over array elements and possibly storing results in temporary
|
| 199 |
+
# variables or accumulate it in the assign_to object.
|
| 200 |
+
|
| 201 |
+
if self._settings.get('contract', True):
|
| 202 |
+
from sympy.tensor import get_contraction_structure
|
| 203 |
+
# Setup loops over non-dummy indices -- all terms need these
|
| 204 |
+
indices = self._get_expression_indices(expr, assign_to)
|
| 205 |
+
# Setup loops over dummy indices -- each term needs separate treatment
|
| 206 |
+
dummies = get_contraction_structure(expr)
|
| 207 |
+
else:
|
| 208 |
+
indices = []
|
| 209 |
+
dummies = {None: (expr,)}
|
| 210 |
+
openloop, closeloop = self._get_loop_opening_ending(indices)
|
| 211 |
+
|
| 212 |
+
# terms with no summations first
|
| 213 |
+
if None in dummies:
|
| 214 |
+
text = StrPrinter.doprint(self, Add(*dummies[None]))
|
| 215 |
+
else:
|
| 216 |
+
# If all terms have summations we must initialize array to Zero
|
| 217 |
+
text = StrPrinter.doprint(self, 0)
|
| 218 |
+
|
| 219 |
+
# skip redundant assignments (where lhs == rhs)
|
| 220 |
+
lhs_printed = self._print(assign_to)
|
| 221 |
+
lines = []
|
| 222 |
+
if text != lhs_printed:
|
| 223 |
+
lines.extend(openloop)
|
| 224 |
+
if assign_to is not None:
|
| 225 |
+
text = self._get_statement("%s = %s" % (lhs_printed, text))
|
| 226 |
+
lines.append(text)
|
| 227 |
+
lines.extend(closeloop)
|
| 228 |
+
|
| 229 |
+
# then terms with summations
|
| 230 |
+
for d in dummies:
|
| 231 |
+
if isinstance(d, tuple):
|
| 232 |
+
indices = self._sort_optimized(d, expr)
|
| 233 |
+
openloop_d, closeloop_d = self._get_loop_opening_ending(
|
| 234 |
+
indices)
|
| 235 |
+
|
| 236 |
+
for term in dummies[d]:
|
| 237 |
+
if term in dummies and not ([list(f.keys()) for f in dummies[term]]
|
| 238 |
+
== [[None] for f in dummies[term]]):
|
| 239 |
+
# If one factor in the term has it's own internal
|
| 240 |
+
# contractions, those must be computed first.
|
| 241 |
+
# (temporary variables?)
|
| 242 |
+
raise NotImplementedError(
|
| 243 |
+
"FIXME: no support for contractions in factor yet")
|
| 244 |
+
else:
|
| 245 |
+
|
| 246 |
+
# We need the lhs expression as an accumulator for
|
| 247 |
+
# the loops, i.e
|
| 248 |
+
#
|
| 249 |
+
# for (int d=0; d < dim; d++){
|
| 250 |
+
# lhs[] = lhs[] + term[][d]
|
| 251 |
+
# } ^.................. the accumulator
|
| 252 |
+
#
|
| 253 |
+
# We check if the expression already contains the
|
| 254 |
+
# lhs, and raise an exception if it does, as that
|
| 255 |
+
# syntax is currently undefined. FIXME: What would be
|
| 256 |
+
# a good interpretation?
|
| 257 |
+
if assign_to is None:
|
| 258 |
+
raise AssignmentError(
|
| 259 |
+
"need assignment variable for loops")
|
| 260 |
+
if term.has(assign_to):
|
| 261 |
+
raise ValueError("FIXME: lhs present in rhs,\
|
| 262 |
+
this is undefined in CodePrinter")
|
| 263 |
+
|
| 264 |
+
lines.extend(openloop)
|
| 265 |
+
lines.extend(openloop_d)
|
| 266 |
+
text = "%s = %s" % (lhs_printed, StrPrinter.doprint(
|
| 267 |
+
self, assign_to + term))
|
| 268 |
+
lines.append(self._get_statement(text))
|
| 269 |
+
lines.extend(closeloop_d)
|
| 270 |
+
lines.extend(closeloop)
|
| 271 |
+
|
| 272 |
+
return "\n".join(lines)
|
| 273 |
+
|
| 274 |
+
def _get_expression_indices(self, expr, assign_to):
|
| 275 |
+
from sympy.tensor import get_indices
|
| 276 |
+
rinds, junk = get_indices(expr)
|
| 277 |
+
linds, junk = get_indices(assign_to)
|
| 278 |
+
|
| 279 |
+
# support broadcast of scalar
|
| 280 |
+
if linds and not rinds:
|
| 281 |
+
rinds = linds
|
| 282 |
+
if rinds != linds:
|
| 283 |
+
raise ValueError("lhs indices must match non-dummy"
|
| 284 |
+
" rhs indices in %s" % expr)
|
| 285 |
+
|
| 286 |
+
return self._sort_optimized(rinds, assign_to)
|
| 287 |
+
|
| 288 |
+
def _sort_optimized(self, indices, expr):
|
| 289 |
+
|
| 290 |
+
from sympy.tensor.indexed import Indexed
|
| 291 |
+
|
| 292 |
+
if not indices:
|
| 293 |
+
return []
|
| 294 |
+
|
| 295 |
+
# determine optimized loop order by giving a score to each index
|
| 296 |
+
# the index with the highest score are put in the innermost loop.
|
| 297 |
+
score_table = {}
|
| 298 |
+
for i in indices:
|
| 299 |
+
score_table[i] = 0
|
| 300 |
+
|
| 301 |
+
arrays = expr.atoms(Indexed)
|
| 302 |
+
for arr in arrays:
|
| 303 |
+
for p, ind in enumerate(arr.indices):
|
| 304 |
+
try:
|
| 305 |
+
score_table[ind] += self._rate_index_position(p)
|
| 306 |
+
except KeyError:
|
| 307 |
+
pass
|
| 308 |
+
|
| 309 |
+
return sorted(indices, key=lambda x: score_table[x])
|
| 310 |
+
|
| 311 |
+
def _rate_index_position(self, p):
|
| 312 |
+
"""function to calculate score based on position among indices
|
| 313 |
+
|
| 314 |
+
This method is used to sort loops in an optimized order, see
|
| 315 |
+
CodePrinter._sort_optimized()
|
| 316 |
+
"""
|
| 317 |
+
raise NotImplementedError("This function must be implemented by "
|
| 318 |
+
"subclass of CodePrinter.")
|
| 319 |
+
|
| 320 |
+
def _get_statement(self, codestring):
|
| 321 |
+
"""Formats a codestring with the proper line ending."""
|
| 322 |
+
raise NotImplementedError("This function must be implemented by "
|
| 323 |
+
"subclass of CodePrinter.")
|
| 324 |
+
|
| 325 |
+
def _get_comment(self, text):
|
| 326 |
+
"""Formats a text string as a comment."""
|
| 327 |
+
raise NotImplementedError("This function must be implemented by "
|
| 328 |
+
"subclass of CodePrinter.")
|
| 329 |
+
|
| 330 |
+
def _declare_number_const(self, name, value):
|
| 331 |
+
"""Declare a numeric constant at the top of a function"""
|
| 332 |
+
raise NotImplementedError("This function must be implemented by "
|
| 333 |
+
"subclass of CodePrinter.")
|
| 334 |
+
|
| 335 |
+
def _format_code(self, lines):
|
| 336 |
+
"""Take in a list of lines of code, and format them accordingly.
|
| 337 |
+
|
| 338 |
+
This may include indenting, wrapping long lines, etc..."""
|
| 339 |
+
raise NotImplementedError("This function must be implemented by "
|
| 340 |
+
"subclass of CodePrinter.")
|
| 341 |
+
|
| 342 |
+
def _get_loop_opening_ending(self, indices):
|
| 343 |
+
"""Returns a tuple (open_lines, close_lines) containing lists
|
| 344 |
+
of codelines"""
|
| 345 |
+
raise NotImplementedError("This function must be implemented by "
|
| 346 |
+
"subclass of CodePrinter.")
|
| 347 |
+
|
| 348 |
+
def _print_Dummy(self, expr):
|
| 349 |
+
if expr.name.startswith('Dummy_'):
|
| 350 |
+
return '_' + expr.name
|
| 351 |
+
else:
|
| 352 |
+
return '%s_%d' % (expr.name, expr.dummy_index)
|
| 353 |
+
|
| 354 |
+
def _print_CodeBlock(self, expr):
|
| 355 |
+
return '\n'.join([self._print(i) for i in expr.args])
|
| 356 |
+
|
| 357 |
+
def _print_String(self, string):
|
| 358 |
+
return str(string)
|
| 359 |
+
|
| 360 |
+
def _print_QuotedString(self, arg):
|
| 361 |
+
return '"%s"' % arg.text
|
| 362 |
+
|
| 363 |
+
def _print_Comment(self, string):
|
| 364 |
+
return self._get_comment(str(string))
|
| 365 |
+
|
| 366 |
+
def _print_Assignment(self, expr):
|
| 367 |
+
from sympy.codegen.ast import Assignment
|
| 368 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 369 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 370 |
+
from sympy.tensor.indexed import IndexedBase
|
| 371 |
+
lhs = expr.lhs
|
| 372 |
+
rhs = expr.rhs
|
| 373 |
+
# We special case assignments that take multiple lines
|
| 374 |
+
if isinstance(expr.rhs, Piecewise):
|
| 375 |
+
# Here we modify Piecewise so each expression is now
|
| 376 |
+
# an Assignment, and then continue on the print.
|
| 377 |
+
expressions = []
|
| 378 |
+
conditions = []
|
| 379 |
+
for (e, c) in rhs.args:
|
| 380 |
+
expressions.append(Assignment(lhs, e))
|
| 381 |
+
conditions.append(c)
|
| 382 |
+
temp = Piecewise(*zip(expressions, conditions))
|
| 383 |
+
return self._print(temp)
|
| 384 |
+
elif isinstance(lhs, MatrixSymbol):
|
| 385 |
+
# Here we form an Assignment for each element in the array,
|
| 386 |
+
# printing each one.
|
| 387 |
+
lines = []
|
| 388 |
+
for (i, j) in self._traverse_matrix_indices(lhs):
|
| 389 |
+
temp = Assignment(lhs[i, j], rhs[i, j])
|
| 390 |
+
code0 = self._print(temp)
|
| 391 |
+
lines.append(code0)
|
| 392 |
+
return "\n".join(lines)
|
| 393 |
+
elif self._settings.get("contract", False) and (lhs.has(IndexedBase) or
|
| 394 |
+
rhs.has(IndexedBase)):
|
| 395 |
+
# Here we check if there is looping to be done, and if so
|
| 396 |
+
# print the required loops.
|
| 397 |
+
return self._doprint_loops(rhs, lhs)
|
| 398 |
+
else:
|
| 399 |
+
lhs_code = self._print(lhs)
|
| 400 |
+
rhs_code = self._print(rhs)
|
| 401 |
+
return self._get_statement("%s = %s" % (lhs_code, rhs_code))
|
| 402 |
+
|
| 403 |
+
def _print_AugmentedAssignment(self, expr):
|
| 404 |
+
lhs_code = self._print(expr.lhs)
|
| 405 |
+
rhs_code = self._print(expr.rhs)
|
| 406 |
+
return self._get_statement("{} {} {}".format(
|
| 407 |
+
*(self._print(arg) for arg in [lhs_code, expr.op, rhs_code])))
|
| 408 |
+
|
| 409 |
+
def _print_FunctionCall(self, expr):
|
| 410 |
+
return '%s(%s)' % (
|
| 411 |
+
expr.name,
|
| 412 |
+
', '.join((self._print(arg) for arg in expr.function_args)))
|
| 413 |
+
|
| 414 |
+
def _print_Variable(self, expr):
|
| 415 |
+
return self._print(expr.symbol)
|
| 416 |
+
|
| 417 |
+
def _print_Symbol(self, expr):
|
| 418 |
+
|
| 419 |
+
name = super()._print_Symbol(expr)
|
| 420 |
+
|
| 421 |
+
if name in self.reserved_words:
|
| 422 |
+
if self._settings['error_on_reserved']:
|
| 423 |
+
msg = ('This expression includes the symbol "{}" which is a '
|
| 424 |
+
'reserved keyword in this language.')
|
| 425 |
+
raise ValueError(msg.format(name))
|
| 426 |
+
return name + self._settings['reserved_word_suffix']
|
| 427 |
+
else:
|
| 428 |
+
return name
|
| 429 |
+
|
| 430 |
+
def _can_print(self, name):
|
| 431 |
+
""" Check if function ``name`` is either a known function or has its own
|
| 432 |
+
printing method. Used to check if rewriting is possible."""
|
| 433 |
+
return name in self.known_functions or getattr(self, '_print_{}'.format(name), False)
|
| 434 |
+
|
| 435 |
+
def _print_Function(self, expr):
|
| 436 |
+
if expr.func.__name__ in self.known_functions:
|
| 437 |
+
cond_func = self.known_functions[expr.func.__name__]
|
| 438 |
+
if isinstance(cond_func, str):
|
| 439 |
+
return "%s(%s)" % (cond_func, self.stringify(expr.args, ", "))
|
| 440 |
+
else:
|
| 441 |
+
for cond, func in cond_func:
|
| 442 |
+
if cond(*expr.args):
|
| 443 |
+
break
|
| 444 |
+
if func is not None:
|
| 445 |
+
try:
|
| 446 |
+
return func(*[self.parenthesize(item, 0) for item in expr.args])
|
| 447 |
+
except TypeError:
|
| 448 |
+
return "%s(%s)" % (func, self.stringify(expr.args, ", "))
|
| 449 |
+
elif hasattr(expr, '_imp_') and isinstance(expr._imp_, Lambda):
|
| 450 |
+
# inlined function
|
| 451 |
+
return self._print(expr._imp_(*expr.args))
|
| 452 |
+
elif expr.func.__name__ in self._rewriteable_functions:
|
| 453 |
+
# Simple rewrite to supported function possible
|
| 454 |
+
target_f, required_fs = self._rewriteable_functions[expr.func.__name__]
|
| 455 |
+
if self._can_print(target_f) and all(self._can_print(f) for f in required_fs):
|
| 456 |
+
return '(' + self._print(expr.rewrite(target_f)) + ')'
|
| 457 |
+
|
| 458 |
+
if expr.is_Function and self._settings.get('allow_unknown_functions', False):
|
| 459 |
+
return '%s(%s)' % (self._print(expr.func), ', '.join(map(self._print, expr.args)))
|
| 460 |
+
else:
|
| 461 |
+
return self._print_not_supported(expr)
|
| 462 |
+
|
| 463 |
+
_print_Expr = _print_Function
|
| 464 |
+
|
| 465 |
+
# Don't inherit the str-printer method for Heaviside to the code printers
|
| 466 |
+
_print_Heaviside = None
|
| 467 |
+
|
| 468 |
+
def _print_NumberSymbol(self, expr):
|
| 469 |
+
if self._settings.get("inline", False):
|
| 470 |
+
return self._print(Float(expr.evalf(self._settings["precision"])))
|
| 471 |
+
else:
|
| 472 |
+
# A Number symbol that is not implemented here or with _printmethod
|
| 473 |
+
# is registered and evaluated
|
| 474 |
+
self._number_symbols.add((expr,
|
| 475 |
+
Float(expr.evalf(self._settings["precision"]))))
|
| 476 |
+
return str(expr)
|
| 477 |
+
|
| 478 |
+
def _print_Catalan(self, expr):
|
| 479 |
+
return self._print_NumberSymbol(expr)
|
| 480 |
+
def _print_EulerGamma(self, expr):
|
| 481 |
+
return self._print_NumberSymbol(expr)
|
| 482 |
+
def _print_GoldenRatio(self, expr):
|
| 483 |
+
return self._print_NumberSymbol(expr)
|
| 484 |
+
def _print_TribonacciConstant(self, expr):
|
| 485 |
+
return self._print_NumberSymbol(expr)
|
| 486 |
+
def _print_Exp1(self, expr):
|
| 487 |
+
return self._print_NumberSymbol(expr)
|
| 488 |
+
def _print_Pi(self, expr):
|
| 489 |
+
return self._print_NumberSymbol(expr)
|
| 490 |
+
|
| 491 |
+
def _print_And(self, expr):
|
| 492 |
+
PREC = precedence(expr)
|
| 493 |
+
return (" %s " % self._operators['and']).join(self.parenthesize(a, PREC)
|
| 494 |
+
for a in sorted(expr.args, key=default_sort_key))
|
| 495 |
+
|
| 496 |
+
def _print_Or(self, expr):
|
| 497 |
+
PREC = precedence(expr)
|
| 498 |
+
return (" %s " % self._operators['or']).join(self.parenthesize(a, PREC)
|
| 499 |
+
for a in sorted(expr.args, key=default_sort_key))
|
| 500 |
+
|
| 501 |
+
def _print_Xor(self, expr):
|
| 502 |
+
if self._operators.get('xor') is None:
|
| 503 |
+
return self._print(expr.to_nnf())
|
| 504 |
+
PREC = precedence(expr)
|
| 505 |
+
return (" %s " % self._operators['xor']).join(self.parenthesize(a, PREC)
|
| 506 |
+
for a in expr.args)
|
| 507 |
+
|
| 508 |
+
def _print_Equivalent(self, expr):
|
| 509 |
+
if self._operators.get('equivalent') is None:
|
| 510 |
+
return self._print(expr.to_nnf())
|
| 511 |
+
PREC = precedence(expr)
|
| 512 |
+
return (" %s " % self._operators['equivalent']).join(self.parenthesize(a, PREC)
|
| 513 |
+
for a in expr.args)
|
| 514 |
+
|
| 515 |
+
def _print_Not(self, expr):
|
| 516 |
+
PREC = precedence(expr)
|
| 517 |
+
return self._operators['not'] + self.parenthesize(expr.args[0], PREC)
|
| 518 |
+
|
| 519 |
+
def _print_BooleanFunction(self, expr):
|
| 520 |
+
return self._print(expr.to_nnf())
|
| 521 |
+
|
| 522 |
+
def _print_Mul(self, expr):
|
| 523 |
+
|
| 524 |
+
prec = precedence(expr)
|
| 525 |
+
|
| 526 |
+
c, e = expr.as_coeff_Mul()
|
| 527 |
+
if c < 0:
|
| 528 |
+
expr = _keep_coeff(-c, e)
|
| 529 |
+
sign = "-"
|
| 530 |
+
else:
|
| 531 |
+
sign = ""
|
| 532 |
+
|
| 533 |
+
a = [] # items in the numerator
|
| 534 |
+
b = [] # items that are in the denominator (if any)
|
| 535 |
+
|
| 536 |
+
pow_paren = [] # Will collect all pow with more than one base element and exp = -1
|
| 537 |
+
|
| 538 |
+
if self.order not in ('old', 'none'):
|
| 539 |
+
args = expr.as_ordered_factors()
|
| 540 |
+
else:
|
| 541 |
+
# use make_args in case expr was something like -x -> x
|
| 542 |
+
args = Mul.make_args(expr)
|
| 543 |
+
|
| 544 |
+
# Gather args for numerator/denominator
|
| 545 |
+
for item in args:
|
| 546 |
+
if item.is_commutative and item.is_Pow and item.exp.is_Rational and item.exp.is_negative:
|
| 547 |
+
if item.exp != -1:
|
| 548 |
+
b.append(Pow(item.base, -item.exp, evaluate=False))
|
| 549 |
+
else:
|
| 550 |
+
if len(item.args[0].args) != 1 and isinstance(item.base, Mul): # To avoid situations like #14160
|
| 551 |
+
pow_paren.append(item)
|
| 552 |
+
b.append(Pow(item.base, -item.exp))
|
| 553 |
+
else:
|
| 554 |
+
a.append(item)
|
| 555 |
+
|
| 556 |
+
a = a or [S.One]
|
| 557 |
+
|
| 558 |
+
if len(a) == 1 and sign == "-":
|
| 559 |
+
# Unary minus does not have a SymPy class, and hence there's no
|
| 560 |
+
# precedence weight associated with it, Python's unary minus has
|
| 561 |
+
# an operator precedence between multiplication and exponentiation,
|
| 562 |
+
# so we use this to compute a weight.
|
| 563 |
+
a_str = [self.parenthesize(a[0], 0.5*(PRECEDENCE["Pow"]+PRECEDENCE["Mul"]))]
|
| 564 |
+
else:
|
| 565 |
+
a_str = [self.parenthesize(x, prec) for x in a]
|
| 566 |
+
b_str = [self.parenthesize(x, prec) for x in b]
|
| 567 |
+
|
| 568 |
+
# To parenthesize Pow with exp = -1 and having more than one Symbol
|
| 569 |
+
for item in pow_paren:
|
| 570 |
+
if item.base in b:
|
| 571 |
+
b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)]
|
| 572 |
+
|
| 573 |
+
if not b:
|
| 574 |
+
return sign + '*'.join(a_str)
|
| 575 |
+
elif len(b) == 1:
|
| 576 |
+
return sign + '*'.join(a_str) + "/" + b_str[0]
|
| 577 |
+
else:
|
| 578 |
+
return sign + '*'.join(a_str) + "/(%s)" % '*'.join(b_str)
|
| 579 |
+
|
| 580 |
+
def _print_not_supported(self, expr):
|
| 581 |
+
if self._settings.get('strict', False):
|
| 582 |
+
raise PrintMethodNotImplementedError("Unsupported by %s: %s" % (str(type(self)), str(type(expr))) + \
|
| 583 |
+
"\nSet the printer option 'strict' to False in order to generate partially printed code.")
|
| 584 |
+
try:
|
| 585 |
+
self._not_supported.add(expr)
|
| 586 |
+
except TypeError:
|
| 587 |
+
# not hashable
|
| 588 |
+
pass
|
| 589 |
+
return self.emptyPrinter(expr)
|
| 590 |
+
|
| 591 |
+
# The following can not be simply translated into C or Fortran
|
| 592 |
+
_print_Basic = _print_not_supported
|
| 593 |
+
_print_ComplexInfinity = _print_not_supported
|
| 594 |
+
_print_Derivative = _print_not_supported
|
| 595 |
+
_print_ExprCondPair = _print_not_supported
|
| 596 |
+
_print_GeometryEntity = _print_not_supported
|
| 597 |
+
_print_Infinity = _print_not_supported
|
| 598 |
+
_print_Integral = _print_not_supported
|
| 599 |
+
_print_Interval = _print_not_supported
|
| 600 |
+
_print_AccumulationBounds = _print_not_supported
|
| 601 |
+
_print_Limit = _print_not_supported
|
| 602 |
+
_print_MatrixBase = _print_not_supported
|
| 603 |
+
_print_DeferredVector = _print_not_supported
|
| 604 |
+
_print_NaN = _print_not_supported
|
| 605 |
+
_print_NegativeInfinity = _print_not_supported
|
| 606 |
+
_print_Order = _print_not_supported
|
| 607 |
+
_print_RootOf = _print_not_supported
|
| 608 |
+
_print_RootsOf = _print_not_supported
|
| 609 |
+
_print_RootSum = _print_not_supported
|
| 610 |
+
_print_Uniform = _print_not_supported
|
| 611 |
+
_print_Unit = _print_not_supported
|
| 612 |
+
_print_Wild = _print_not_supported
|
| 613 |
+
_print_WildFunction = _print_not_supported
|
| 614 |
+
_print_Relational = _print_not_supported
|
| 615 |
+
|
| 616 |
+
|
| 617 |
+
# Code printer functions. These are included in this file so that they can be
|
| 618 |
+
# imported in the top-level __init__.py without importing the sympy.codegen
|
| 619 |
+
# module.
|
| 620 |
+
|
| 621 |
+
def ccode(expr, assign_to=None, standard='c99', **settings):
|
| 622 |
+
"""Converts an expr to a string of c code
|
| 623 |
+
|
| 624 |
+
Parameters
|
| 625 |
+
==========
|
| 626 |
+
|
| 627 |
+
expr : Expr
|
| 628 |
+
A SymPy expression to be converted.
|
| 629 |
+
assign_to : optional
|
| 630 |
+
When given, the argument is used as the name of the variable to which
|
| 631 |
+
the expression is assigned. Can be a string, ``Symbol``,
|
| 632 |
+
``MatrixSymbol``, or ``Indexed`` type. This is helpful in case of
|
| 633 |
+
line-wrapping, or for expressions that generate multi-line statements.
|
| 634 |
+
standard : str, optional
|
| 635 |
+
String specifying the standard. If your compiler supports a more modern
|
| 636 |
+
standard you may set this to 'c99' to allow the printer to use more math
|
| 637 |
+
functions. [default='c89'].
|
| 638 |
+
precision : integer, optional
|
| 639 |
+
The precision for numbers such as pi [default=17].
|
| 640 |
+
user_functions : dict, optional
|
| 641 |
+
A dictionary where the keys are string representations of either
|
| 642 |
+
``FunctionClass`` or ``UndefinedFunction`` instances and the values
|
| 643 |
+
are their desired C string representations. Alternatively, the
|
| 644 |
+
dictionary value can be a list of tuples i.e. [(argument_test,
|
| 645 |
+
cfunction_string)] or [(argument_test, cfunction_formater)]. See below
|
| 646 |
+
for examples.
|
| 647 |
+
dereference : iterable, optional
|
| 648 |
+
An iterable of symbols that should be dereferenced in the printed code
|
| 649 |
+
expression. These would be values passed by address to the function.
|
| 650 |
+
For example, if ``dereference=[a]``, the resulting code would print
|
| 651 |
+
``(*a)`` instead of ``a``.
|
| 652 |
+
human : bool, optional
|
| 653 |
+
If True, the result is a single string that may contain some constant
|
| 654 |
+
declarations for the number symbols. If False, the same information is
|
| 655 |
+
returned in a tuple of (symbols_to_declare, not_supported_functions,
|
| 656 |
+
code_text). [default=True].
|
| 657 |
+
contract: bool, optional
|
| 658 |
+
If True, ``Indexed`` instances are assumed to obey tensor contraction
|
| 659 |
+
rules and the corresponding nested loops over indices are generated.
|
| 660 |
+
Setting contract=False will not generate loops, instead the user is
|
| 661 |
+
responsible to provide values for the indices in the code.
|
| 662 |
+
[default=True].
|
| 663 |
+
|
| 664 |
+
Examples
|
| 665 |
+
========
|
| 666 |
+
|
| 667 |
+
>>> from sympy import ccode, symbols, Rational, sin, ceiling, Abs, Function
|
| 668 |
+
>>> x, tau = symbols("x, tau")
|
| 669 |
+
>>> expr = (2*tau)**Rational(7, 2)
|
| 670 |
+
>>> ccode(expr)
|
| 671 |
+
'8*M_SQRT2*pow(tau, 7.0/2.0)'
|
| 672 |
+
>>> ccode(expr, math_macros={})
|
| 673 |
+
'8*sqrt(2)*pow(tau, 7.0/2.0)'
|
| 674 |
+
>>> ccode(sin(x), assign_to="s")
|
| 675 |
+
's = sin(x);'
|
| 676 |
+
>>> from sympy.codegen.ast import real, float80
|
| 677 |
+
>>> ccode(expr, type_aliases={real: float80})
|
| 678 |
+
'8*M_SQRT2l*powl(tau, 7.0L/2.0L)'
|
| 679 |
+
|
| 680 |
+
Simple custom printing can be defined for certain types by passing a
|
| 681 |
+
dictionary of {"type" : "function"} to the ``user_functions`` kwarg.
|
| 682 |
+
Alternatively, the dictionary value can be a list of tuples i.e.
|
| 683 |
+
[(argument_test, cfunction_string)].
|
| 684 |
+
|
| 685 |
+
>>> custom_functions = {
|
| 686 |
+
... "ceiling": "CEIL",
|
| 687 |
+
... "Abs": [(lambda x: not x.is_integer, "fabs"),
|
| 688 |
+
... (lambda x: x.is_integer, "ABS")],
|
| 689 |
+
... "func": "f"
|
| 690 |
+
... }
|
| 691 |
+
>>> func = Function('func')
|
| 692 |
+
>>> ccode(func(Abs(x) + ceiling(x)), standard='C89', user_functions=custom_functions)
|
| 693 |
+
'f(fabs(x) + CEIL(x))'
|
| 694 |
+
|
| 695 |
+
or if the C-function takes a subset of the original arguments:
|
| 696 |
+
|
| 697 |
+
>>> ccode(2**x + 3**x, standard='C99', user_functions={'Pow': [
|
| 698 |
+
... (lambda b, e: b == 2, lambda b, e: 'exp2(%s)' % e),
|
| 699 |
+
... (lambda b, e: b != 2, 'pow')]})
|
| 700 |
+
'exp2(x) + pow(3, x)'
|
| 701 |
+
|
| 702 |
+
``Piecewise`` expressions are converted into conditionals. If an
|
| 703 |
+
``assign_to`` variable is provided an if statement is created, otherwise
|
| 704 |
+
the ternary operator is used. Note that if the ``Piecewise`` lacks a
|
| 705 |
+
default term, represented by ``(expr, True)`` then an error will be thrown.
|
| 706 |
+
This is to prevent generating an expression that may not evaluate to
|
| 707 |
+
anything.
|
| 708 |
+
|
| 709 |
+
>>> from sympy import Piecewise
|
| 710 |
+
>>> expr = Piecewise((x + 1, x > 0), (x, True))
|
| 711 |
+
>>> print(ccode(expr, tau, standard='C89'))
|
| 712 |
+
if (x > 0) {
|
| 713 |
+
tau = x + 1;
|
| 714 |
+
}
|
| 715 |
+
else {
|
| 716 |
+
tau = x;
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
Support for loops is provided through ``Indexed`` types. With
|
| 720 |
+
``contract=True`` these expressions will be turned into loops, whereas
|
| 721 |
+
``contract=False`` will just print the assignment expression that should be
|
| 722 |
+
looped over:
|
| 723 |
+
|
| 724 |
+
>>> from sympy import Eq, IndexedBase, Idx
|
| 725 |
+
>>> len_y = 5
|
| 726 |
+
>>> y = IndexedBase('y', shape=(len_y,))
|
| 727 |
+
>>> t = IndexedBase('t', shape=(len_y,))
|
| 728 |
+
>>> Dy = IndexedBase('Dy', shape=(len_y-1,))
|
| 729 |
+
>>> i = Idx('i', len_y-1)
|
| 730 |
+
>>> e=Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i]))
|
| 731 |
+
>>> ccode(e.rhs, assign_to=e.lhs, contract=False, standard='C89')
|
| 732 |
+
'Dy[i] = (y[i + 1] - y[i])/(t[i + 1] - t[i]);'
|
| 733 |
+
|
| 734 |
+
Matrices are also supported, but a ``MatrixSymbol`` of the same dimensions
|
| 735 |
+
must be provided to ``assign_to``. Note that any expression that can be
|
| 736 |
+
generated normally can also exist inside a Matrix:
|
| 737 |
+
|
| 738 |
+
>>> from sympy import Matrix, MatrixSymbol
|
| 739 |
+
>>> mat = Matrix([x**2, Piecewise((x + 1, x > 0), (x, True)), sin(x)])
|
| 740 |
+
>>> A = MatrixSymbol('A', 3, 1)
|
| 741 |
+
>>> print(ccode(mat, A, standard='C89'))
|
| 742 |
+
A[0] = pow(x, 2);
|
| 743 |
+
if (x > 0) {
|
| 744 |
+
A[1] = x + 1;
|
| 745 |
+
}
|
| 746 |
+
else {
|
| 747 |
+
A[1] = x;
|
| 748 |
+
}
|
| 749 |
+
A[2] = sin(x);
|
| 750 |
+
"""
|
| 751 |
+
from sympy.printing.c import c_code_printers
|
| 752 |
+
return c_code_printers[standard.lower()](settings).doprint(expr, assign_to)
|
| 753 |
+
|
| 754 |
+
def print_ccode(expr, **settings):
|
| 755 |
+
"""Prints C representation of the given expression."""
|
| 756 |
+
print(ccode(expr, **settings))
|
| 757 |
+
|
| 758 |
+
def fcode(expr, assign_to=None, **settings):
|
| 759 |
+
"""Converts an expr to a string of fortran code
|
| 760 |
+
|
| 761 |
+
Parameters
|
| 762 |
+
==========
|
| 763 |
+
|
| 764 |
+
expr : Expr
|
| 765 |
+
A SymPy expression to be converted.
|
| 766 |
+
assign_to : optional
|
| 767 |
+
When given, the argument is used as the name of the variable to which
|
| 768 |
+
the expression is assigned. Can be a string, ``Symbol``,
|
| 769 |
+
``MatrixSymbol``, or ``Indexed`` type. This is helpful in case of
|
| 770 |
+
line-wrapping, or for expressions that generate multi-line statements.
|
| 771 |
+
precision : integer, optional
|
| 772 |
+
DEPRECATED. Use type_mappings instead. The precision for numbers such
|
| 773 |
+
as pi [default=17].
|
| 774 |
+
user_functions : dict, optional
|
| 775 |
+
A dictionary where keys are ``FunctionClass`` instances and values are
|
| 776 |
+
their string representations. Alternatively, the dictionary value can
|
| 777 |
+
be a list of tuples i.e. [(argument_test, cfunction_string)]. See below
|
| 778 |
+
for examples.
|
| 779 |
+
human : bool, optional
|
| 780 |
+
If True, the result is a single string that may contain some constant
|
| 781 |
+
declarations for the number symbols. If False, the same information is
|
| 782 |
+
returned in a tuple of (symbols_to_declare, not_supported_functions,
|
| 783 |
+
code_text). [default=True].
|
| 784 |
+
contract: bool, optional
|
| 785 |
+
If True, ``Indexed`` instances are assumed to obey tensor contraction
|
| 786 |
+
rules and the corresponding nested loops over indices are generated.
|
| 787 |
+
Setting contract=False will not generate loops, instead the user is
|
| 788 |
+
responsible to provide values for the indices in the code.
|
| 789 |
+
[default=True].
|
| 790 |
+
source_format : optional
|
| 791 |
+
The source format can be either 'fixed' or 'free'. [default='fixed']
|
| 792 |
+
standard : integer, optional
|
| 793 |
+
The Fortran standard to be followed. This is specified as an integer.
|
| 794 |
+
Acceptable standards are 66, 77, 90, 95, 2003, and 2008. Default is 77.
|
| 795 |
+
Note that currently the only distinction internally is between
|
| 796 |
+
standards before 95, and those 95 and after. This may change later as
|
| 797 |
+
more features are added.
|
| 798 |
+
name_mangling : bool, optional
|
| 799 |
+
If True, then the variables that would become identical in
|
| 800 |
+
case-insensitive Fortran are mangled by appending different number
|
| 801 |
+
of ``_`` at the end. If False, SymPy Will not interfere with naming of
|
| 802 |
+
variables. [default=True]
|
| 803 |
+
|
| 804 |
+
Examples
|
| 805 |
+
========
|
| 806 |
+
|
| 807 |
+
>>> from sympy import fcode, symbols, Rational, sin, ceiling, floor
|
| 808 |
+
>>> x, tau = symbols("x, tau")
|
| 809 |
+
>>> fcode((2*tau)**Rational(7, 2))
|
| 810 |
+
' 8*sqrt(2.0d0)*tau**(7.0d0/2.0d0)'
|
| 811 |
+
>>> fcode(sin(x), assign_to="s")
|
| 812 |
+
' s = sin(x)'
|
| 813 |
+
|
| 814 |
+
Custom printing can be defined for certain types by passing a dictionary of
|
| 815 |
+
"type" : "function" to the ``user_functions`` kwarg. Alternatively, the
|
| 816 |
+
dictionary value can be a list of tuples i.e. [(argument_test,
|
| 817 |
+
cfunction_string)].
|
| 818 |
+
|
| 819 |
+
>>> custom_functions = {
|
| 820 |
+
... "ceiling": "CEIL",
|
| 821 |
+
... "floor": [(lambda x: not x.is_integer, "FLOOR1"),
|
| 822 |
+
... (lambda x: x.is_integer, "FLOOR2")]
|
| 823 |
+
... }
|
| 824 |
+
>>> fcode(floor(x) + ceiling(x), user_functions=custom_functions)
|
| 825 |
+
' CEIL(x) + FLOOR1(x)'
|
| 826 |
+
|
| 827 |
+
``Piecewise`` expressions are converted into conditionals. If an
|
| 828 |
+
``assign_to`` variable is provided an if statement is created, otherwise
|
| 829 |
+
the ternary operator is used. Note that if the ``Piecewise`` lacks a
|
| 830 |
+
default term, represented by ``(expr, True)`` then an error will be thrown.
|
| 831 |
+
This is to prevent generating an expression that may not evaluate to
|
| 832 |
+
anything.
|
| 833 |
+
|
| 834 |
+
>>> from sympy import Piecewise
|
| 835 |
+
>>> expr = Piecewise((x + 1, x > 0), (x, True))
|
| 836 |
+
>>> print(fcode(expr, tau))
|
| 837 |
+
if (x > 0) then
|
| 838 |
+
tau = x + 1
|
| 839 |
+
else
|
| 840 |
+
tau = x
|
| 841 |
+
end if
|
| 842 |
+
|
| 843 |
+
Support for loops is provided through ``Indexed`` types. With
|
| 844 |
+
``contract=True`` these expressions will be turned into loops, whereas
|
| 845 |
+
``contract=False`` will just print the assignment expression that should be
|
| 846 |
+
looped over:
|
| 847 |
+
|
| 848 |
+
>>> from sympy import Eq, IndexedBase, Idx
|
| 849 |
+
>>> len_y = 5
|
| 850 |
+
>>> y = IndexedBase('y', shape=(len_y,))
|
| 851 |
+
>>> t = IndexedBase('t', shape=(len_y,))
|
| 852 |
+
>>> Dy = IndexedBase('Dy', shape=(len_y-1,))
|
| 853 |
+
>>> i = Idx('i', len_y-1)
|
| 854 |
+
>>> e=Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i]))
|
| 855 |
+
>>> fcode(e.rhs, assign_to=e.lhs, contract=False)
|
| 856 |
+
' Dy(i) = (y(i + 1) - y(i))/(t(i + 1) - t(i))'
|
| 857 |
+
|
| 858 |
+
Matrices are also supported, but a ``MatrixSymbol`` of the same dimensions
|
| 859 |
+
must be provided to ``assign_to``. Note that any expression that can be
|
| 860 |
+
generated normally can also exist inside a Matrix:
|
| 861 |
+
|
| 862 |
+
>>> from sympy import Matrix, MatrixSymbol
|
| 863 |
+
>>> mat = Matrix([x**2, Piecewise((x + 1, x > 0), (x, True)), sin(x)])
|
| 864 |
+
>>> A = MatrixSymbol('A', 3, 1)
|
| 865 |
+
>>> print(fcode(mat, A))
|
| 866 |
+
A(1, 1) = x**2
|
| 867 |
+
if (x > 0) then
|
| 868 |
+
A(2, 1) = x + 1
|
| 869 |
+
else
|
| 870 |
+
A(2, 1) = x
|
| 871 |
+
end if
|
| 872 |
+
A(3, 1) = sin(x)
|
| 873 |
+
"""
|
| 874 |
+
from sympy.printing.fortran import FCodePrinter
|
| 875 |
+
return FCodePrinter(settings).doprint(expr, assign_to)
|
| 876 |
+
|
| 877 |
+
|
| 878 |
+
def print_fcode(expr, **settings):
|
| 879 |
+
"""Prints the Fortran representation of the given expression.
|
| 880 |
+
|
| 881 |
+
See fcode for the meaning of the optional arguments.
|
| 882 |
+
"""
|
| 883 |
+
print(fcode(expr, **settings))
|
| 884 |
+
|
| 885 |
+
def cxxcode(expr, assign_to=None, standard='c++11', **settings):
|
| 886 |
+
""" C++ equivalent of :func:`~.ccode`. """
|
| 887 |
+
from sympy.printing.cxx import cxx_code_printers
|
| 888 |
+
return cxx_code_printers[standard.lower()](settings).doprint(expr, assign_to)
|
lib/python3.12/site-packages/sympy/printing/octave.py
ADDED
|
@@ -0,0 +1,715 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Octave (and Matlab) code printer
|
| 3 |
+
|
| 4 |
+
The `OctaveCodePrinter` converts SymPy expressions into Octave expressions.
|
| 5 |
+
It uses a subset of the Octave language for Matlab compatibility.
|
| 6 |
+
|
| 7 |
+
A complete code generator, which uses `octave_code` extensively, can be found
|
| 8 |
+
in `sympy.utilities.codegen`. The `codegen` module can be used to generate
|
| 9 |
+
complete source code files.
|
| 10 |
+
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
from typing import Any
|
| 15 |
+
|
| 16 |
+
from sympy.core import Mul, Pow, S, Rational
|
| 17 |
+
from sympy.core.mul import _keep_coeff
|
| 18 |
+
from sympy.core.numbers import equal_valued
|
| 19 |
+
from sympy.printing.codeprinter import CodePrinter
|
| 20 |
+
from sympy.printing.precedence import precedence, PRECEDENCE
|
| 21 |
+
from re import search
|
| 22 |
+
|
| 23 |
+
# List of known functions. First, those that have the same name in
|
| 24 |
+
# SymPy and Octave. This is almost certainly incomplete!
|
| 25 |
+
known_fcns_src1 = ["sin", "cos", "tan", "cot", "sec", "csc",
|
| 26 |
+
"asin", "acos", "acot", "atan", "atan2", "asec", "acsc",
|
| 27 |
+
"sinh", "cosh", "tanh", "coth", "csch", "sech",
|
| 28 |
+
"asinh", "acosh", "atanh", "acoth", "asech", "acsch",
|
| 29 |
+
"erfc", "erfi", "erf", "erfinv", "erfcinv",
|
| 30 |
+
"besseli", "besselj", "besselk", "bessely",
|
| 31 |
+
"bernoulli", "beta", "euler", "exp", "factorial", "floor",
|
| 32 |
+
"fresnelc", "fresnels", "gamma", "harmonic", "log",
|
| 33 |
+
"polylog", "sign", "zeta", "legendre"]
|
| 34 |
+
|
| 35 |
+
# These functions have different names ("SymPy": "Octave"), more
|
| 36 |
+
# generally a mapping to (argument_conditions, octave_function).
|
| 37 |
+
known_fcns_src2 = {
|
| 38 |
+
"Abs": "abs",
|
| 39 |
+
"arg": "angle", # arg/angle ok in Octave but only angle in Matlab
|
| 40 |
+
"binomial": "bincoeff",
|
| 41 |
+
"ceiling": "ceil",
|
| 42 |
+
"chebyshevu": "chebyshevU",
|
| 43 |
+
"chebyshevt": "chebyshevT",
|
| 44 |
+
"Chi": "coshint",
|
| 45 |
+
"Ci": "cosint",
|
| 46 |
+
"conjugate": "conj",
|
| 47 |
+
"DiracDelta": "dirac",
|
| 48 |
+
"Heaviside": "heaviside",
|
| 49 |
+
"im": "imag",
|
| 50 |
+
"laguerre": "laguerreL",
|
| 51 |
+
"LambertW": "lambertw",
|
| 52 |
+
"li": "logint",
|
| 53 |
+
"loggamma": "gammaln",
|
| 54 |
+
"Max": "max",
|
| 55 |
+
"Min": "min",
|
| 56 |
+
"Mod": "mod",
|
| 57 |
+
"polygamma": "psi",
|
| 58 |
+
"re": "real",
|
| 59 |
+
"RisingFactorial": "pochhammer",
|
| 60 |
+
"Shi": "sinhint",
|
| 61 |
+
"Si": "sinint",
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class OctaveCodePrinter(CodePrinter):
|
| 66 |
+
"""
|
| 67 |
+
A printer to convert expressions to strings of Octave/Matlab code.
|
| 68 |
+
"""
|
| 69 |
+
printmethod = "_octave"
|
| 70 |
+
language = "Octave"
|
| 71 |
+
|
| 72 |
+
_operators = {
|
| 73 |
+
'and': '&',
|
| 74 |
+
'or': '|',
|
| 75 |
+
'not': '~',
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
_default_settings: dict[str, Any] = dict(CodePrinter._default_settings, **{
|
| 79 |
+
'precision': 17,
|
| 80 |
+
'user_functions': {},
|
| 81 |
+
'contract': True,
|
| 82 |
+
'inline': True,
|
| 83 |
+
})
|
| 84 |
+
# Note: contract is for expressing tensors as loops (if True), or just
|
| 85 |
+
# assignment (if False). FIXME: this should be looked a more carefully
|
| 86 |
+
# for Octave.
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def __init__(self, settings={}):
|
| 90 |
+
super().__init__(settings)
|
| 91 |
+
self.known_functions = dict(zip(known_fcns_src1, known_fcns_src1))
|
| 92 |
+
self.known_functions.update(dict(known_fcns_src2))
|
| 93 |
+
userfuncs = settings.get('user_functions', {})
|
| 94 |
+
self.known_functions.update(userfuncs)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def _rate_index_position(self, p):
|
| 98 |
+
return p*5
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def _get_statement(self, codestring):
|
| 102 |
+
return "%s;" % codestring
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def _get_comment(self, text):
|
| 106 |
+
return "% {}".format(text)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def _declare_number_const(self, name, value):
|
| 110 |
+
return "{} = {};".format(name, value)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def _format_code(self, lines):
|
| 114 |
+
return self.indent_code(lines)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def _traverse_matrix_indices(self, mat):
|
| 118 |
+
# Octave uses Fortran order (column-major)
|
| 119 |
+
rows, cols = mat.shape
|
| 120 |
+
return ((i, j) for j in range(cols) for i in range(rows))
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def _get_loop_opening_ending(self, indices):
|
| 124 |
+
open_lines = []
|
| 125 |
+
close_lines = []
|
| 126 |
+
for i in indices:
|
| 127 |
+
# Octave arrays start at 1 and end at dimension
|
| 128 |
+
var, start, stop = map(self._print,
|
| 129 |
+
[i.label, i.lower + 1, i.upper + 1])
|
| 130 |
+
open_lines.append("for %s = %s:%s" % (var, start, stop))
|
| 131 |
+
close_lines.append("end")
|
| 132 |
+
return open_lines, close_lines
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _print_Mul(self, expr):
|
| 136 |
+
# print complex numbers nicely in Octave
|
| 137 |
+
if (expr.is_number and expr.is_imaginary and
|
| 138 |
+
(S.ImaginaryUnit*expr).is_Integer):
|
| 139 |
+
return "%si" % self._print(-S.ImaginaryUnit*expr)
|
| 140 |
+
|
| 141 |
+
# cribbed from str.py
|
| 142 |
+
prec = precedence(expr)
|
| 143 |
+
|
| 144 |
+
c, e = expr.as_coeff_Mul()
|
| 145 |
+
if c < 0:
|
| 146 |
+
expr = _keep_coeff(-c, e)
|
| 147 |
+
sign = "-"
|
| 148 |
+
else:
|
| 149 |
+
sign = ""
|
| 150 |
+
|
| 151 |
+
a = [] # items in the numerator
|
| 152 |
+
b = [] # items that are in the denominator (if any)
|
| 153 |
+
|
| 154 |
+
pow_paren = [] # Will collect all pow with more than one base element and exp = -1
|
| 155 |
+
|
| 156 |
+
if self.order not in ('old', 'none'):
|
| 157 |
+
args = expr.as_ordered_factors()
|
| 158 |
+
else:
|
| 159 |
+
# use make_args in case expr was something like -x -> x
|
| 160 |
+
args = Mul.make_args(expr)
|
| 161 |
+
|
| 162 |
+
# Gather args for numerator/denominator
|
| 163 |
+
for item in args:
|
| 164 |
+
if (item.is_commutative and item.is_Pow and item.exp.is_Rational
|
| 165 |
+
and item.exp.is_negative):
|
| 166 |
+
if item.exp != -1:
|
| 167 |
+
b.append(Pow(item.base, -item.exp, evaluate=False))
|
| 168 |
+
else:
|
| 169 |
+
if len(item.args[0].args) != 1 and isinstance(item.base, Mul): # To avoid situations like #14160
|
| 170 |
+
pow_paren.append(item)
|
| 171 |
+
b.append(Pow(item.base, -item.exp))
|
| 172 |
+
elif item.is_Rational and item is not S.Infinity:
|
| 173 |
+
if item.p != 1:
|
| 174 |
+
a.append(Rational(item.p))
|
| 175 |
+
if item.q != 1:
|
| 176 |
+
b.append(Rational(item.q))
|
| 177 |
+
else:
|
| 178 |
+
a.append(item)
|
| 179 |
+
|
| 180 |
+
a = a or [S.One]
|
| 181 |
+
|
| 182 |
+
a_str = [self.parenthesize(x, prec) for x in a]
|
| 183 |
+
b_str = [self.parenthesize(x, prec) for x in b]
|
| 184 |
+
|
| 185 |
+
# To parenthesize Pow with exp = -1 and having more than one Symbol
|
| 186 |
+
for item in pow_paren:
|
| 187 |
+
if item.base in b:
|
| 188 |
+
b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)]
|
| 189 |
+
|
| 190 |
+
# from here it differs from str.py to deal with "*" and ".*"
|
| 191 |
+
def multjoin(a, a_str):
|
| 192 |
+
# here we probably are assuming the constants will come first
|
| 193 |
+
r = a_str[0]
|
| 194 |
+
for i in range(1, len(a)):
|
| 195 |
+
mulsym = '*' if a[i-1].is_number else '.*'
|
| 196 |
+
r = r + mulsym + a_str[i]
|
| 197 |
+
return r
|
| 198 |
+
|
| 199 |
+
if not b:
|
| 200 |
+
return sign + multjoin(a, a_str)
|
| 201 |
+
elif len(b) == 1:
|
| 202 |
+
divsym = '/' if b[0].is_number else './'
|
| 203 |
+
return sign + multjoin(a, a_str) + divsym + b_str[0]
|
| 204 |
+
else:
|
| 205 |
+
divsym = '/' if all(bi.is_number for bi in b) else './'
|
| 206 |
+
return (sign + multjoin(a, a_str) +
|
| 207 |
+
divsym + "(%s)" % multjoin(b, b_str))
|
| 208 |
+
|
| 209 |
+
def _print_Relational(self, expr):
|
| 210 |
+
lhs_code = self._print(expr.lhs)
|
| 211 |
+
rhs_code = self._print(expr.rhs)
|
| 212 |
+
op = expr.rel_op
|
| 213 |
+
return "{} {} {}".format(lhs_code, op, rhs_code)
|
| 214 |
+
|
| 215 |
+
def _print_Pow(self, expr):
|
| 216 |
+
powsymbol = '^' if all(x.is_number for x in expr.args) else '.^'
|
| 217 |
+
|
| 218 |
+
PREC = precedence(expr)
|
| 219 |
+
|
| 220 |
+
if equal_valued(expr.exp, 0.5):
|
| 221 |
+
return "sqrt(%s)" % self._print(expr.base)
|
| 222 |
+
|
| 223 |
+
if expr.is_commutative:
|
| 224 |
+
if equal_valued(expr.exp, -0.5):
|
| 225 |
+
sym = '/' if expr.base.is_number else './'
|
| 226 |
+
return "1" + sym + "sqrt(%s)" % self._print(expr.base)
|
| 227 |
+
if equal_valued(expr.exp, -1):
|
| 228 |
+
sym = '/' if expr.base.is_number else './'
|
| 229 |
+
return "1" + sym + "%s" % self.parenthesize(expr.base, PREC)
|
| 230 |
+
|
| 231 |
+
return '%s%s%s' % (self.parenthesize(expr.base, PREC), powsymbol,
|
| 232 |
+
self.parenthesize(expr.exp, PREC))
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def _print_MatPow(self, expr):
|
| 236 |
+
PREC = precedence(expr)
|
| 237 |
+
return '%s^%s' % (self.parenthesize(expr.base, PREC),
|
| 238 |
+
self.parenthesize(expr.exp, PREC))
|
| 239 |
+
|
| 240 |
+
def _print_MatrixSolve(self, expr):
|
| 241 |
+
PREC = precedence(expr)
|
| 242 |
+
return "%s \\ %s" % (self.parenthesize(expr.matrix, PREC),
|
| 243 |
+
self.parenthesize(expr.vector, PREC))
|
| 244 |
+
|
| 245 |
+
def _print_Pi(self, expr):
|
| 246 |
+
return 'pi'
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def _print_ImaginaryUnit(self, expr):
|
| 250 |
+
return "1i"
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def _print_Exp1(self, expr):
|
| 254 |
+
return "exp(1)"
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def _print_GoldenRatio(self, expr):
|
| 258 |
+
# FIXME: how to do better, e.g., for octave_code(2*GoldenRatio)?
|
| 259 |
+
#return self._print((1+sqrt(S(5)))/2)
|
| 260 |
+
return "(1+sqrt(5))/2"
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def _print_Assignment(self, expr):
|
| 264 |
+
from sympy.codegen.ast import Assignment
|
| 265 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 266 |
+
from sympy.tensor.indexed import IndexedBase
|
| 267 |
+
# Copied from codeprinter, but remove special MatrixSymbol treatment
|
| 268 |
+
lhs = expr.lhs
|
| 269 |
+
rhs = expr.rhs
|
| 270 |
+
# We special case assignments that take multiple lines
|
| 271 |
+
if not self._settings["inline"] and isinstance(expr.rhs, Piecewise):
|
| 272 |
+
# Here we modify Piecewise so each expression is now
|
| 273 |
+
# an Assignment, and then continue on the print.
|
| 274 |
+
expressions = []
|
| 275 |
+
conditions = []
|
| 276 |
+
for (e, c) in rhs.args:
|
| 277 |
+
expressions.append(Assignment(lhs, e))
|
| 278 |
+
conditions.append(c)
|
| 279 |
+
temp = Piecewise(*zip(expressions, conditions))
|
| 280 |
+
return self._print(temp)
|
| 281 |
+
if self._settings["contract"] and (lhs.has(IndexedBase) or
|
| 282 |
+
rhs.has(IndexedBase)):
|
| 283 |
+
# Here we check if there is looping to be done, and if so
|
| 284 |
+
# print the required loops.
|
| 285 |
+
return self._doprint_loops(rhs, lhs)
|
| 286 |
+
else:
|
| 287 |
+
lhs_code = self._print(lhs)
|
| 288 |
+
rhs_code = self._print(rhs)
|
| 289 |
+
return self._get_statement("%s = %s" % (lhs_code, rhs_code))
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
def _print_Infinity(self, expr):
|
| 293 |
+
return 'inf'
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
def _print_NegativeInfinity(self, expr):
|
| 297 |
+
return '-inf'
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def _print_NaN(self, expr):
|
| 301 |
+
return 'NaN'
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def _print_list(self, expr):
|
| 305 |
+
return '{' + ', '.join(self._print(a) for a in expr) + '}'
|
| 306 |
+
_print_tuple = _print_list
|
| 307 |
+
_print_Tuple = _print_list
|
| 308 |
+
_print_List = _print_list
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
def _print_BooleanTrue(self, expr):
|
| 312 |
+
return "true"
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def _print_BooleanFalse(self, expr):
|
| 316 |
+
return "false"
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
def _print_bool(self, expr):
|
| 320 |
+
return str(expr).lower()
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
# Could generate quadrature code for definite Integrals?
|
| 324 |
+
#_print_Integral = _print_not_supported
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
def _print_MatrixBase(self, A):
|
| 328 |
+
# Handle zero dimensions:
|
| 329 |
+
if (A.rows, A.cols) == (0, 0):
|
| 330 |
+
return '[]'
|
| 331 |
+
elif S.Zero in A.shape:
|
| 332 |
+
return 'zeros(%s, %s)' % (A.rows, A.cols)
|
| 333 |
+
elif (A.rows, A.cols) == (1, 1):
|
| 334 |
+
# Octave does not distinguish between scalars and 1x1 matrices
|
| 335 |
+
return self._print(A[0, 0])
|
| 336 |
+
return "[%s]" % "; ".join(" ".join([self._print(a) for a in A[r, :]])
|
| 337 |
+
for r in range(A.rows))
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
def _print_SparseRepMatrix(self, A):
|
| 341 |
+
from sympy.matrices import Matrix
|
| 342 |
+
L = A.col_list();
|
| 343 |
+
# make row vectors of the indices and entries
|
| 344 |
+
I = Matrix([[k[0] + 1 for k in L]])
|
| 345 |
+
J = Matrix([[k[1] + 1 for k in L]])
|
| 346 |
+
AIJ = Matrix([[k[2] for k in L]])
|
| 347 |
+
return "sparse(%s, %s, %s, %s, %s)" % (self._print(I), self._print(J),
|
| 348 |
+
self._print(AIJ), A.rows, A.cols)
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
def _print_MatrixElement(self, expr):
|
| 352 |
+
return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) \
|
| 353 |
+
+ '(%s, %s)' % (expr.i + 1, expr.j + 1)
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
def _print_MatrixSlice(self, expr):
|
| 357 |
+
def strslice(x, lim):
|
| 358 |
+
l = x[0] + 1
|
| 359 |
+
h = x[1]
|
| 360 |
+
step = x[2]
|
| 361 |
+
lstr = self._print(l)
|
| 362 |
+
hstr = 'end' if h == lim else self._print(h)
|
| 363 |
+
if step == 1:
|
| 364 |
+
if l == 1 and h == lim:
|
| 365 |
+
return ':'
|
| 366 |
+
if l == h:
|
| 367 |
+
return lstr
|
| 368 |
+
else:
|
| 369 |
+
return lstr + ':' + hstr
|
| 370 |
+
else:
|
| 371 |
+
return ':'.join((lstr, self._print(step), hstr))
|
| 372 |
+
return (self._print(expr.parent) + '(' +
|
| 373 |
+
strslice(expr.rowslice, expr.parent.shape[0]) + ', ' +
|
| 374 |
+
strslice(expr.colslice, expr.parent.shape[1]) + ')')
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
def _print_Indexed(self, expr):
|
| 378 |
+
inds = [ self._print(i) for i in expr.indices ]
|
| 379 |
+
return "%s(%s)" % (self._print(expr.base.label), ", ".join(inds))
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
def _print_Idx(self, expr):
|
| 383 |
+
return self._print(expr.label)
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def _print_KroneckerDelta(self, expr):
|
| 387 |
+
prec = PRECEDENCE["Pow"]
|
| 388 |
+
return "double(%s == %s)" % tuple(self.parenthesize(x, prec)
|
| 389 |
+
for x in expr.args)
|
| 390 |
+
|
| 391 |
+
def _print_HadamardProduct(self, expr):
|
| 392 |
+
return '.*'.join([self.parenthesize(arg, precedence(expr))
|
| 393 |
+
for arg in expr.args])
|
| 394 |
+
|
| 395 |
+
def _print_HadamardPower(self, expr):
|
| 396 |
+
PREC = precedence(expr)
|
| 397 |
+
return '.**'.join([
|
| 398 |
+
self.parenthesize(expr.base, PREC),
|
| 399 |
+
self.parenthesize(expr.exp, PREC)
|
| 400 |
+
])
|
| 401 |
+
|
| 402 |
+
def _print_Identity(self, expr):
|
| 403 |
+
shape = expr.shape
|
| 404 |
+
if len(shape) == 2 and shape[0] == shape[1]:
|
| 405 |
+
shape = [shape[0]]
|
| 406 |
+
s = ", ".join(self._print(n) for n in shape)
|
| 407 |
+
return "eye(" + s + ")"
|
| 408 |
+
|
| 409 |
+
def _print_lowergamma(self, expr):
|
| 410 |
+
# Octave implements regularized incomplete gamma function
|
| 411 |
+
return "(gammainc({1}, {0}).*gamma({0}))".format(
|
| 412 |
+
self._print(expr.args[0]), self._print(expr.args[1]))
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
def _print_uppergamma(self, expr):
|
| 416 |
+
return "(gammainc({1}, {0}, 'upper').*gamma({0}))".format(
|
| 417 |
+
self._print(expr.args[0]), self._print(expr.args[1]))
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
def _print_sinc(self, expr):
|
| 421 |
+
#Note: Divide by pi because Octave implements normalized sinc function.
|
| 422 |
+
return "sinc(%s)" % self._print(expr.args[0]/S.Pi)
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
def _print_hankel1(self, expr):
|
| 426 |
+
return "besselh(%s, 1, %s)" % (self._print(expr.order),
|
| 427 |
+
self._print(expr.argument))
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
def _print_hankel2(self, expr):
|
| 431 |
+
return "besselh(%s, 2, %s)" % (self._print(expr.order),
|
| 432 |
+
self._print(expr.argument))
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
# Note: as of 2015, Octave doesn't have spherical Bessel functions
|
| 436 |
+
def _print_jn(self, expr):
|
| 437 |
+
from sympy.functions import sqrt, besselj
|
| 438 |
+
x = expr.argument
|
| 439 |
+
expr2 = sqrt(S.Pi/(2*x))*besselj(expr.order + S.Half, x)
|
| 440 |
+
return self._print(expr2)
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
def _print_yn(self, expr):
|
| 444 |
+
from sympy.functions import sqrt, bessely
|
| 445 |
+
x = expr.argument
|
| 446 |
+
expr2 = sqrt(S.Pi/(2*x))*bessely(expr.order + S.Half, x)
|
| 447 |
+
return self._print(expr2)
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
def _print_airyai(self, expr):
|
| 451 |
+
return "airy(0, %s)" % self._print(expr.args[0])
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
def _print_airyaiprime(self, expr):
|
| 455 |
+
return "airy(1, %s)" % self._print(expr.args[0])
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def _print_airybi(self, expr):
|
| 459 |
+
return "airy(2, %s)" % self._print(expr.args[0])
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
def _print_airybiprime(self, expr):
|
| 463 |
+
return "airy(3, %s)" % self._print(expr.args[0])
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
def _print_expint(self, expr):
|
| 467 |
+
mu, x = expr.args
|
| 468 |
+
if mu != 1:
|
| 469 |
+
return self._print_not_supported(expr)
|
| 470 |
+
return "expint(%s)" % self._print(x)
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
def _one_or_two_reversed_args(self, expr):
|
| 474 |
+
assert len(expr.args) <= 2
|
| 475 |
+
return '{name}({args})'.format(
|
| 476 |
+
name=self.known_functions[expr.__class__.__name__],
|
| 477 |
+
args=", ".join([self._print(x) for x in reversed(expr.args)])
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
_print_DiracDelta = _print_LambertW = _one_or_two_reversed_args
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
def _nested_binary_math_func(self, expr):
|
| 485 |
+
return '{name}({arg1}, {arg2})'.format(
|
| 486 |
+
name=self.known_functions[expr.__class__.__name__],
|
| 487 |
+
arg1=self._print(expr.args[0]),
|
| 488 |
+
arg2=self._print(expr.func(*expr.args[1:]))
|
| 489 |
+
)
|
| 490 |
+
|
| 491 |
+
_print_Max = _print_Min = _nested_binary_math_func
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
def _print_Piecewise(self, expr):
|
| 495 |
+
if expr.args[-1].cond != True:
|
| 496 |
+
# We need the last conditional to be a True, otherwise the resulting
|
| 497 |
+
# function may not return a result.
|
| 498 |
+
raise ValueError("All Piecewise expressions must contain an "
|
| 499 |
+
"(expr, True) statement to be used as a default "
|
| 500 |
+
"condition. Without one, the generated "
|
| 501 |
+
"expression may not evaluate to anything under "
|
| 502 |
+
"some condition.")
|
| 503 |
+
lines = []
|
| 504 |
+
if self._settings["inline"]:
|
| 505 |
+
# Express each (cond, expr) pair in a nested Horner form:
|
| 506 |
+
# (condition) .* (expr) + (not cond) .* (<others>)
|
| 507 |
+
# Expressions that result in multiple statements won't work here.
|
| 508 |
+
ecpairs = ["({0}).*({1}) + (~({0})).*(".format
|
| 509 |
+
(self._print(c), self._print(e))
|
| 510 |
+
for e, c in expr.args[:-1]]
|
| 511 |
+
elast = "%s" % self._print(expr.args[-1].expr)
|
| 512 |
+
pw = " ...\n".join(ecpairs) + elast + ")"*len(ecpairs)
|
| 513 |
+
# Note: current need these outer brackets for 2*pw. Would be
|
| 514 |
+
# nicer to teach parenthesize() to do this for us when needed!
|
| 515 |
+
return "(" + pw + ")"
|
| 516 |
+
else:
|
| 517 |
+
for i, (e, c) in enumerate(expr.args):
|
| 518 |
+
if i == 0:
|
| 519 |
+
lines.append("if (%s)" % self._print(c))
|
| 520 |
+
elif i == len(expr.args) - 1 and c == True:
|
| 521 |
+
lines.append("else")
|
| 522 |
+
else:
|
| 523 |
+
lines.append("elseif (%s)" % self._print(c))
|
| 524 |
+
code0 = self._print(e)
|
| 525 |
+
lines.append(code0)
|
| 526 |
+
if i == len(expr.args) - 1:
|
| 527 |
+
lines.append("end")
|
| 528 |
+
return "\n".join(lines)
|
| 529 |
+
|
| 530 |
+
|
| 531 |
+
def _print_zeta(self, expr):
|
| 532 |
+
if len(expr.args) == 1:
|
| 533 |
+
return "zeta(%s)" % self._print(expr.args[0])
|
| 534 |
+
else:
|
| 535 |
+
# Matlab two argument zeta is not equivalent to SymPy's
|
| 536 |
+
return self._print_not_supported(expr)
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
def indent_code(self, code):
|
| 540 |
+
"""Accepts a string of code or a list of code lines"""
|
| 541 |
+
|
| 542 |
+
# code mostly copied from ccode
|
| 543 |
+
if isinstance(code, str):
|
| 544 |
+
code_lines = self.indent_code(code.splitlines(True))
|
| 545 |
+
return ''.join(code_lines)
|
| 546 |
+
|
| 547 |
+
tab = " "
|
| 548 |
+
inc_regex = ('^function ', '^if ', '^elseif ', '^else$', '^for ')
|
| 549 |
+
dec_regex = ('^end$', '^elseif ', '^else$')
|
| 550 |
+
|
| 551 |
+
# pre-strip left-space from the code
|
| 552 |
+
code = [ line.lstrip(' \t') for line in code ]
|
| 553 |
+
|
| 554 |
+
increase = [ int(any(search(re, line) for re in inc_regex))
|
| 555 |
+
for line in code ]
|
| 556 |
+
decrease = [ int(any(search(re, line) for re in dec_regex))
|
| 557 |
+
for line in code ]
|
| 558 |
+
|
| 559 |
+
pretty = []
|
| 560 |
+
level = 0
|
| 561 |
+
for n, line in enumerate(code):
|
| 562 |
+
if line in ('', '\n'):
|
| 563 |
+
pretty.append(line)
|
| 564 |
+
continue
|
| 565 |
+
level -= decrease[n]
|
| 566 |
+
pretty.append("%s%s" % (tab*level, line))
|
| 567 |
+
level += increase[n]
|
| 568 |
+
return pretty
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
def octave_code(expr, assign_to=None, **settings):
|
| 572 |
+
r"""Converts `expr` to a string of Octave (or Matlab) code.
|
| 573 |
+
|
| 574 |
+
The string uses a subset of the Octave language for Matlab compatibility.
|
| 575 |
+
|
| 576 |
+
Parameters
|
| 577 |
+
==========
|
| 578 |
+
|
| 579 |
+
expr : Expr
|
| 580 |
+
A SymPy expression to be converted.
|
| 581 |
+
assign_to : optional
|
| 582 |
+
When given, the argument is used as the name of the variable to which
|
| 583 |
+
the expression is assigned. Can be a string, ``Symbol``,
|
| 584 |
+
``MatrixSymbol``, or ``Indexed`` type. This can be helpful for
|
| 585 |
+
expressions that generate multi-line statements.
|
| 586 |
+
precision : integer, optional
|
| 587 |
+
The precision for numbers such as pi [default=16].
|
| 588 |
+
user_functions : dict, optional
|
| 589 |
+
A dictionary where keys are ``FunctionClass`` instances and values are
|
| 590 |
+
their string representations. Alternatively, the dictionary value can
|
| 591 |
+
be a list of tuples i.e. [(argument_test, cfunction_string)]. See
|
| 592 |
+
below for examples.
|
| 593 |
+
human : bool, optional
|
| 594 |
+
If True, the result is a single string that may contain some constant
|
| 595 |
+
declarations for the number symbols. If False, the same information is
|
| 596 |
+
returned in a tuple of (symbols_to_declare, not_supported_functions,
|
| 597 |
+
code_text). [default=True].
|
| 598 |
+
contract: bool, optional
|
| 599 |
+
If True, ``Indexed`` instances are assumed to obey tensor contraction
|
| 600 |
+
rules and the corresponding nested loops over indices are generated.
|
| 601 |
+
Setting contract=False will not generate loops, instead the user is
|
| 602 |
+
responsible to provide values for the indices in the code.
|
| 603 |
+
[default=True].
|
| 604 |
+
inline: bool, optional
|
| 605 |
+
If True, we try to create single-statement code instead of multiple
|
| 606 |
+
statements. [default=True].
|
| 607 |
+
|
| 608 |
+
Examples
|
| 609 |
+
========
|
| 610 |
+
|
| 611 |
+
>>> from sympy import octave_code, symbols, sin, pi
|
| 612 |
+
>>> x = symbols('x')
|
| 613 |
+
>>> octave_code(sin(x).series(x).removeO())
|
| 614 |
+
'x.^5/120 - x.^3/6 + x'
|
| 615 |
+
|
| 616 |
+
>>> from sympy import Rational, ceiling
|
| 617 |
+
>>> x, y, tau = symbols("x, y, tau")
|
| 618 |
+
>>> octave_code((2*tau)**Rational(7, 2))
|
| 619 |
+
'8*sqrt(2)*tau.^(7/2)'
|
| 620 |
+
|
| 621 |
+
Note that element-wise (Hadamard) operations are used by default between
|
| 622 |
+
symbols. This is because its very common in Octave to write "vectorized"
|
| 623 |
+
code. It is harmless if the values are scalars.
|
| 624 |
+
|
| 625 |
+
>>> octave_code(sin(pi*x*y), assign_to="s")
|
| 626 |
+
's = sin(pi*x.*y);'
|
| 627 |
+
|
| 628 |
+
If you need a matrix product "*" or matrix power "^", you can specify the
|
| 629 |
+
symbol as a ``MatrixSymbol``.
|
| 630 |
+
|
| 631 |
+
>>> from sympy import Symbol, MatrixSymbol
|
| 632 |
+
>>> n = Symbol('n', integer=True, positive=True)
|
| 633 |
+
>>> A = MatrixSymbol('A', n, n)
|
| 634 |
+
>>> octave_code(3*pi*A**3)
|
| 635 |
+
'(3*pi)*A^3'
|
| 636 |
+
|
| 637 |
+
This class uses several rules to decide which symbol to use a product.
|
| 638 |
+
Pure numbers use "*", Symbols use ".*" and MatrixSymbols use "*".
|
| 639 |
+
A HadamardProduct can be used to specify componentwise multiplication ".*"
|
| 640 |
+
of two MatrixSymbols. There is currently there is no easy way to specify
|
| 641 |
+
scalar symbols, so sometimes the code might have some minor cosmetic
|
| 642 |
+
issues. For example, suppose x and y are scalars and A is a Matrix, then
|
| 643 |
+
while a human programmer might write "(x^2*y)*A^3", we generate:
|
| 644 |
+
|
| 645 |
+
>>> octave_code(x**2*y*A**3)
|
| 646 |
+
'(x.^2.*y)*A^3'
|
| 647 |
+
|
| 648 |
+
Matrices are supported using Octave inline notation. When using
|
| 649 |
+
``assign_to`` with matrices, the name can be specified either as a string
|
| 650 |
+
or as a ``MatrixSymbol``. The dimensions must align in the latter case.
|
| 651 |
+
|
| 652 |
+
>>> from sympy import Matrix, MatrixSymbol
|
| 653 |
+
>>> mat = Matrix([[x**2, sin(x), ceiling(x)]])
|
| 654 |
+
>>> octave_code(mat, assign_to='A')
|
| 655 |
+
'A = [x.^2 sin(x) ceil(x)];'
|
| 656 |
+
|
| 657 |
+
``Piecewise`` expressions are implemented with logical masking by default.
|
| 658 |
+
Alternatively, you can pass "inline=False" to use if-else conditionals.
|
| 659 |
+
Note that if the ``Piecewise`` lacks a default term, represented by
|
| 660 |
+
``(expr, True)`` then an error will be thrown. This is to prevent
|
| 661 |
+
generating an expression that may not evaluate to anything.
|
| 662 |
+
|
| 663 |
+
>>> from sympy import Piecewise
|
| 664 |
+
>>> pw = Piecewise((x + 1, x > 0), (x, True))
|
| 665 |
+
>>> octave_code(pw, assign_to=tau)
|
| 666 |
+
'tau = ((x > 0).*(x + 1) + (~(x > 0)).*(x));'
|
| 667 |
+
|
| 668 |
+
Note that any expression that can be generated normally can also exist
|
| 669 |
+
inside a Matrix:
|
| 670 |
+
|
| 671 |
+
>>> mat = Matrix([[x**2, pw, sin(x)]])
|
| 672 |
+
>>> octave_code(mat, assign_to='A')
|
| 673 |
+
'A = [x.^2 ((x > 0).*(x + 1) + (~(x > 0)).*(x)) sin(x)];'
|
| 674 |
+
|
| 675 |
+
Custom printing can be defined for certain types by passing a dictionary of
|
| 676 |
+
"type" : "function" to the ``user_functions`` kwarg. Alternatively, the
|
| 677 |
+
dictionary value can be a list of tuples i.e., [(argument_test,
|
| 678 |
+
cfunction_string)]. This can be used to call a custom Octave function.
|
| 679 |
+
|
| 680 |
+
>>> from sympy import Function
|
| 681 |
+
>>> f = Function('f')
|
| 682 |
+
>>> g = Function('g')
|
| 683 |
+
>>> custom_functions = {
|
| 684 |
+
... "f": "existing_octave_fcn",
|
| 685 |
+
... "g": [(lambda x: x.is_Matrix, "my_mat_fcn"),
|
| 686 |
+
... (lambda x: not x.is_Matrix, "my_fcn")]
|
| 687 |
+
... }
|
| 688 |
+
>>> mat = Matrix([[1, x]])
|
| 689 |
+
>>> octave_code(f(x) + g(x) + g(mat), user_functions=custom_functions)
|
| 690 |
+
'existing_octave_fcn(x) + my_fcn(x) + my_mat_fcn([1 x])'
|
| 691 |
+
|
| 692 |
+
Support for loops is provided through ``Indexed`` types. With
|
| 693 |
+
``contract=True`` these expressions will be turned into loops, whereas
|
| 694 |
+
``contract=False`` will just print the assignment expression that should be
|
| 695 |
+
looped over:
|
| 696 |
+
|
| 697 |
+
>>> from sympy import Eq, IndexedBase, Idx
|
| 698 |
+
>>> len_y = 5
|
| 699 |
+
>>> y = IndexedBase('y', shape=(len_y,))
|
| 700 |
+
>>> t = IndexedBase('t', shape=(len_y,))
|
| 701 |
+
>>> Dy = IndexedBase('Dy', shape=(len_y-1,))
|
| 702 |
+
>>> i = Idx('i', len_y-1)
|
| 703 |
+
>>> e = Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i]))
|
| 704 |
+
>>> octave_code(e.rhs, assign_to=e.lhs, contract=False)
|
| 705 |
+
'Dy(i) = (y(i + 1) - y(i))./(t(i + 1) - t(i));'
|
| 706 |
+
"""
|
| 707 |
+
return OctaveCodePrinter(settings).doprint(expr, assign_to)
|
| 708 |
+
|
| 709 |
+
|
| 710 |
+
def print_octave_code(expr, **settings):
|
| 711 |
+
"""Prints the Octave (or Matlab) representation of the given expression.
|
| 712 |
+
|
| 713 |
+
See `octave_code` for the meaning of the optional arguments.
|
| 714 |
+
"""
|
| 715 |
+
print(octave_code(expr, **settings))
|
lib/python3.12/site-packages/sympy/printing/printer.py
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Printing subsystem driver
|
| 2 |
+
|
| 3 |
+
SymPy's printing system works the following way: Any expression can be
|
| 4 |
+
passed to a designated Printer who then is responsible to return an
|
| 5 |
+
adequate representation of that expression.
|
| 6 |
+
|
| 7 |
+
**The basic concept is the following:**
|
| 8 |
+
|
| 9 |
+
1. Let the object print itself if it knows how.
|
| 10 |
+
2. Take the best fitting method defined in the printer.
|
| 11 |
+
3. As fall-back use the emptyPrinter method for the printer.
|
| 12 |
+
|
| 13 |
+
Which Method is Responsible for Printing?
|
| 14 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 15 |
+
|
| 16 |
+
The whole printing process is started by calling ``.doprint(expr)`` on the printer
|
| 17 |
+
which you want to use. This method looks for an appropriate method which can
|
| 18 |
+
print the given expression in the given style that the printer defines.
|
| 19 |
+
While looking for the method, it follows these steps:
|
| 20 |
+
|
| 21 |
+
1. **Let the object print itself if it knows how.**
|
| 22 |
+
|
| 23 |
+
The printer looks for a specific method in every object. The name of that method
|
| 24 |
+
depends on the specific printer and is defined under ``Printer.printmethod``.
|
| 25 |
+
For example, StrPrinter calls ``_sympystr`` and LatexPrinter calls ``_latex``.
|
| 26 |
+
Look at the documentation of the printer that you want to use.
|
| 27 |
+
The name of the method is specified there.
|
| 28 |
+
|
| 29 |
+
This was the original way of doing printing in sympy. Every class had
|
| 30 |
+
its own latex, mathml, str and repr methods, but it turned out that it
|
| 31 |
+
is hard to produce a high quality printer, if all the methods are spread
|
| 32 |
+
out that far. Therefore all printing code was combined into the different
|
| 33 |
+
printers, which works great for built-in SymPy objects, but not that
|
| 34 |
+
good for user defined classes where it is inconvenient to patch the
|
| 35 |
+
printers.
|
| 36 |
+
|
| 37 |
+
2. **Take the best fitting method defined in the printer.**
|
| 38 |
+
|
| 39 |
+
The printer loops through expr classes (class + its bases), and tries
|
| 40 |
+
to dispatch the work to ``_print_<EXPR_CLASS>``
|
| 41 |
+
|
| 42 |
+
e.g., suppose we have the following class hierarchy::
|
| 43 |
+
|
| 44 |
+
Basic
|
| 45 |
+
|
|
| 46 |
+
Atom
|
| 47 |
+
|
|
| 48 |
+
Number
|
| 49 |
+
|
|
| 50 |
+
Rational
|
| 51 |
+
|
| 52 |
+
then, for ``expr=Rational(...)``, the Printer will try
|
| 53 |
+
to call printer methods in the order as shown in the figure below::
|
| 54 |
+
|
| 55 |
+
p._print(expr)
|
| 56 |
+
|
|
| 57 |
+
|-- p._print_Rational(expr)
|
| 58 |
+
|
|
| 59 |
+
|-- p._print_Number(expr)
|
| 60 |
+
|
|
| 61 |
+
|-- p._print_Atom(expr)
|
| 62 |
+
|
|
| 63 |
+
`-- p._print_Basic(expr)
|
| 64 |
+
|
| 65 |
+
if ``._print_Rational`` method exists in the printer, then it is called,
|
| 66 |
+
and the result is returned back. Otherwise, the printer tries to call
|
| 67 |
+
``._print_Number`` and so on.
|
| 68 |
+
|
| 69 |
+
3. **As a fall-back use the emptyPrinter method for the printer.**
|
| 70 |
+
|
| 71 |
+
As fall-back ``self.emptyPrinter`` will be called with the expression. If
|
| 72 |
+
not defined in the Printer subclass this will be the same as ``str(expr)``.
|
| 73 |
+
|
| 74 |
+
.. _printer_example:
|
| 75 |
+
|
| 76 |
+
Example of Custom Printer
|
| 77 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 78 |
+
|
| 79 |
+
In the example below, we have a printer which prints the derivative of a function
|
| 80 |
+
in a shorter form.
|
| 81 |
+
|
| 82 |
+
.. code-block:: python
|
| 83 |
+
|
| 84 |
+
from sympy.core.symbol import Symbol
|
| 85 |
+
from sympy.printing.latex import LatexPrinter, print_latex
|
| 86 |
+
from sympy.core.function import UndefinedFunction, Function
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class MyLatexPrinter(LatexPrinter):
|
| 90 |
+
\"\"\"Print derivative of a function of symbols in a shorter form.
|
| 91 |
+
\"\"\"
|
| 92 |
+
def _print_Derivative(self, expr):
|
| 93 |
+
function, *vars = expr.args
|
| 94 |
+
if not isinstance(type(function), UndefinedFunction) or \\
|
| 95 |
+
not all(isinstance(i, Symbol) for i in vars):
|
| 96 |
+
return super()._print_Derivative(expr)
|
| 97 |
+
|
| 98 |
+
# If you want the printer to work correctly for nested
|
| 99 |
+
# expressions then use self._print() instead of str() or latex().
|
| 100 |
+
# See the example of nested modulo below in the custom printing
|
| 101 |
+
# method section.
|
| 102 |
+
return "{}_{{{}}}".format(
|
| 103 |
+
self._print(Symbol(function.func.__name__)),
|
| 104 |
+
''.join(self._print(i) for i in vars))
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def print_my_latex(expr):
|
| 108 |
+
\"\"\" Most of the printers define their own wrappers for print().
|
| 109 |
+
These wrappers usually take printer settings. Our printer does not have
|
| 110 |
+
any settings.
|
| 111 |
+
\"\"\"
|
| 112 |
+
print(MyLatexPrinter().doprint(expr))
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
y = Symbol("y")
|
| 116 |
+
x = Symbol("x")
|
| 117 |
+
f = Function("f")
|
| 118 |
+
expr = f(x, y).diff(x, y)
|
| 119 |
+
|
| 120 |
+
# Print the expression using the normal latex printer and our custom
|
| 121 |
+
# printer.
|
| 122 |
+
print_latex(expr)
|
| 123 |
+
print_my_latex(expr)
|
| 124 |
+
|
| 125 |
+
The output of the code above is::
|
| 126 |
+
|
| 127 |
+
\\frac{\\partial^{2}}{\\partial x\\partial y} f{\\left(x,y \\right)}
|
| 128 |
+
f_{xy}
|
| 129 |
+
|
| 130 |
+
.. _printer_method_example:
|
| 131 |
+
|
| 132 |
+
Example of Custom Printing Method
|
| 133 |
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| 134 |
+
|
| 135 |
+
In the example below, the latex printing of the modulo operator is modified.
|
| 136 |
+
This is done by overriding the method ``_latex`` of ``Mod``.
|
| 137 |
+
|
| 138 |
+
>>> from sympy import Symbol, Mod, Integer, print_latex
|
| 139 |
+
|
| 140 |
+
>>> # Always use printer._print()
|
| 141 |
+
>>> class ModOp(Mod):
|
| 142 |
+
... def _latex(self, printer):
|
| 143 |
+
... a, b = [printer._print(i) for i in self.args]
|
| 144 |
+
... return r"\\operatorname{Mod}{\\left(%s, %s\\right)}" % (a, b)
|
| 145 |
+
|
| 146 |
+
Comparing the output of our custom operator to the builtin one:
|
| 147 |
+
|
| 148 |
+
>>> x = Symbol('x')
|
| 149 |
+
>>> m = Symbol('m')
|
| 150 |
+
>>> print_latex(Mod(x, m))
|
| 151 |
+
x \\bmod m
|
| 152 |
+
>>> print_latex(ModOp(x, m))
|
| 153 |
+
\\operatorname{Mod}{\\left(x, m\\right)}
|
| 154 |
+
|
| 155 |
+
Common mistakes
|
| 156 |
+
~~~~~~~~~~~~~~~
|
| 157 |
+
It's important to always use ``self._print(obj)`` to print subcomponents of
|
| 158 |
+
an expression when customizing a printer. Mistakes include:
|
| 159 |
+
|
| 160 |
+
1. Using ``self.doprint(obj)`` instead:
|
| 161 |
+
|
| 162 |
+
>>> # This example does not work properly, as only the outermost call may use
|
| 163 |
+
>>> # doprint.
|
| 164 |
+
>>> class ModOpModeWrong(Mod):
|
| 165 |
+
... def _latex(self, printer):
|
| 166 |
+
... a, b = [printer.doprint(i) for i in self.args]
|
| 167 |
+
... return r"\\operatorname{Mod}{\\left(%s, %s\\right)}" % (a, b)
|
| 168 |
+
|
| 169 |
+
This fails when the ``mode`` argument is passed to the printer:
|
| 170 |
+
|
| 171 |
+
>>> print_latex(ModOp(x, m), mode='inline') # ok
|
| 172 |
+
$\\operatorname{Mod}{\\left(x, m\\right)}$
|
| 173 |
+
>>> print_latex(ModOpModeWrong(x, m), mode='inline') # bad
|
| 174 |
+
$\\operatorname{Mod}{\\left($x$, $m$\\right)}$
|
| 175 |
+
|
| 176 |
+
2. Using ``str(obj)`` instead:
|
| 177 |
+
|
| 178 |
+
>>> class ModOpNestedWrong(Mod):
|
| 179 |
+
... def _latex(self, printer):
|
| 180 |
+
... a, b = [str(i) for i in self.args]
|
| 181 |
+
... return r"\\operatorname{Mod}{\\left(%s, %s\\right)}" % (a, b)
|
| 182 |
+
|
| 183 |
+
This fails on nested objects:
|
| 184 |
+
|
| 185 |
+
>>> # Nested modulo.
|
| 186 |
+
>>> print_latex(ModOp(ModOp(x, m), Integer(7))) # ok
|
| 187 |
+
\\operatorname{Mod}{\\left(\\operatorname{Mod}{\\left(x, m\\right)}, 7\\right)}
|
| 188 |
+
>>> print_latex(ModOpNestedWrong(ModOpNestedWrong(x, m), Integer(7))) # bad
|
| 189 |
+
\\operatorname{Mod}{\\left(ModOpNestedWrong(x, m), 7\\right)}
|
| 190 |
+
|
| 191 |
+
3. Using ``LatexPrinter()._print(obj)`` instead.
|
| 192 |
+
|
| 193 |
+
>>> from sympy.printing.latex import LatexPrinter
|
| 194 |
+
>>> class ModOpSettingsWrong(Mod):
|
| 195 |
+
... def _latex(self, printer):
|
| 196 |
+
... a, b = [LatexPrinter()._print(i) for i in self.args]
|
| 197 |
+
... return r"\\operatorname{Mod}{\\left(%s, %s\\right)}" % (a, b)
|
| 198 |
+
|
| 199 |
+
This causes all the settings to be discarded in the subobjects. As an
|
| 200 |
+
example, the ``full_prec`` setting which shows floats to full precision is
|
| 201 |
+
ignored:
|
| 202 |
+
|
| 203 |
+
>>> from sympy import Float
|
| 204 |
+
>>> print_latex(ModOp(Float(1) * x, m), full_prec=True) # ok
|
| 205 |
+
\\operatorname{Mod}{\\left(1.00000000000000 x, m\\right)}
|
| 206 |
+
>>> print_latex(ModOpSettingsWrong(Float(1) * x, m), full_prec=True) # bad
|
| 207 |
+
\\operatorname{Mod}{\\left(1.0 x, m\\right)}
|
| 208 |
+
|
| 209 |
+
"""
|
| 210 |
+
|
| 211 |
+
from __future__ import annotations
|
| 212 |
+
import sys
|
| 213 |
+
from typing import Any, Type
|
| 214 |
+
import inspect
|
| 215 |
+
from contextlib import contextmanager
|
| 216 |
+
from functools import cmp_to_key, update_wrapper
|
| 217 |
+
|
| 218 |
+
from sympy.core.add import Add
|
| 219 |
+
from sympy.core.basic import Basic
|
| 220 |
+
|
| 221 |
+
from sympy.core.function import AppliedUndef, UndefinedFunction, Function
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
@contextmanager
|
| 226 |
+
def printer_context(printer, **kwargs):
|
| 227 |
+
original = printer._context.copy()
|
| 228 |
+
try:
|
| 229 |
+
printer._context.update(kwargs)
|
| 230 |
+
yield
|
| 231 |
+
finally:
|
| 232 |
+
printer._context = original
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class Printer:
|
| 236 |
+
""" Generic printer
|
| 237 |
+
|
| 238 |
+
Its job is to provide infrastructure for implementing new printers easily.
|
| 239 |
+
|
| 240 |
+
If you want to define your custom Printer or your custom printing method
|
| 241 |
+
for your custom class then see the example above: printer_example_ .
|
| 242 |
+
"""
|
| 243 |
+
|
| 244 |
+
_global_settings: dict[str, Any] = {}
|
| 245 |
+
|
| 246 |
+
_default_settings: dict[str, Any] = {}
|
| 247 |
+
|
| 248 |
+
printmethod = None # type: str
|
| 249 |
+
|
| 250 |
+
@classmethod
|
| 251 |
+
def _get_initial_settings(cls):
|
| 252 |
+
settings = cls._default_settings.copy()
|
| 253 |
+
for key, val in cls._global_settings.items():
|
| 254 |
+
if key in cls._default_settings:
|
| 255 |
+
settings[key] = val
|
| 256 |
+
return settings
|
| 257 |
+
|
| 258 |
+
def __init__(self, settings=None):
|
| 259 |
+
self._str = str
|
| 260 |
+
|
| 261 |
+
self._settings = self._get_initial_settings()
|
| 262 |
+
self._context = {} # mutable during printing
|
| 263 |
+
|
| 264 |
+
if settings is not None:
|
| 265 |
+
self._settings.update(settings)
|
| 266 |
+
|
| 267 |
+
if len(self._settings) > len(self._default_settings):
|
| 268 |
+
for key in self._settings:
|
| 269 |
+
if key not in self._default_settings:
|
| 270 |
+
raise TypeError("Unknown setting '%s'." % key)
|
| 271 |
+
|
| 272 |
+
# _print_level is the number of times self._print() was recursively
|
| 273 |
+
# called. See StrPrinter._print_Float() for an example of usage
|
| 274 |
+
self._print_level = 0
|
| 275 |
+
|
| 276 |
+
@classmethod
|
| 277 |
+
def set_global_settings(cls, **settings):
|
| 278 |
+
"""Set system-wide printing settings. """
|
| 279 |
+
for key, val in settings.items():
|
| 280 |
+
if val is not None:
|
| 281 |
+
cls._global_settings[key] = val
|
| 282 |
+
|
| 283 |
+
@property
|
| 284 |
+
def order(self):
|
| 285 |
+
if 'order' in self._settings:
|
| 286 |
+
return self._settings['order']
|
| 287 |
+
else:
|
| 288 |
+
raise AttributeError("No order defined.")
|
| 289 |
+
|
| 290 |
+
def doprint(self, expr):
|
| 291 |
+
"""Returns printer's representation for expr (as a string)"""
|
| 292 |
+
return self._str(self._print(expr))
|
| 293 |
+
|
| 294 |
+
def _print(self, expr, **kwargs) -> str:
|
| 295 |
+
"""Internal dispatcher
|
| 296 |
+
|
| 297 |
+
Tries the following concepts to print an expression:
|
| 298 |
+
1. Let the object print itself if it knows how.
|
| 299 |
+
2. Take the best fitting method defined in the printer.
|
| 300 |
+
3. As fall-back use the emptyPrinter method for the printer.
|
| 301 |
+
"""
|
| 302 |
+
self._print_level += 1
|
| 303 |
+
try:
|
| 304 |
+
# If the printer defines a name for a printing method
|
| 305 |
+
# (Printer.printmethod) and the object knows for itself how it
|
| 306 |
+
# should be printed, use that method.
|
| 307 |
+
if self.printmethod and hasattr(expr, self.printmethod):
|
| 308 |
+
if not (isinstance(expr, type) and issubclass(expr, Basic)):
|
| 309 |
+
return getattr(expr, self.printmethod)(self, **kwargs)
|
| 310 |
+
|
| 311 |
+
# See if the class of expr is known, or if one of its super
|
| 312 |
+
# classes is known, and use that print function
|
| 313 |
+
# Exception: ignore the subclasses of Undefined, so that, e.g.,
|
| 314 |
+
# Function('gamma') does not get dispatched to _print_gamma
|
| 315 |
+
classes = type(expr).__mro__
|
| 316 |
+
if AppliedUndef in classes:
|
| 317 |
+
classes = classes[classes.index(AppliedUndef):]
|
| 318 |
+
if UndefinedFunction in classes:
|
| 319 |
+
classes = classes[classes.index(UndefinedFunction):]
|
| 320 |
+
# Another exception: if someone subclasses a known function, e.g.,
|
| 321 |
+
# gamma, and changes the name, then ignore _print_gamma
|
| 322 |
+
if Function in classes:
|
| 323 |
+
i = classes.index(Function)
|
| 324 |
+
classes = tuple(c for c in classes[:i] if \
|
| 325 |
+
c.__name__ == classes[0].__name__ or \
|
| 326 |
+
c.__name__.endswith("Base")) + classes[i:]
|
| 327 |
+
for cls in classes:
|
| 328 |
+
printmethodname = '_print_' + cls.__name__
|
| 329 |
+
printmethod = getattr(self, printmethodname, None)
|
| 330 |
+
if printmethod is not None:
|
| 331 |
+
return printmethod(expr, **kwargs)
|
| 332 |
+
# Unknown object, fall back to the emptyPrinter.
|
| 333 |
+
return self.emptyPrinter(expr)
|
| 334 |
+
finally:
|
| 335 |
+
self._print_level -= 1
|
| 336 |
+
|
| 337 |
+
def emptyPrinter(self, expr):
|
| 338 |
+
return str(expr)
|
| 339 |
+
|
| 340 |
+
def _as_ordered_terms(self, expr, order=None):
|
| 341 |
+
"""A compatibility function for ordering terms in Add. """
|
| 342 |
+
order = order or self.order
|
| 343 |
+
|
| 344 |
+
if order == 'old':
|
| 345 |
+
return sorted(Add.make_args(expr), key=cmp_to_key(Basic._compare_pretty))
|
| 346 |
+
elif order == 'none':
|
| 347 |
+
return list(expr.args)
|
| 348 |
+
else:
|
| 349 |
+
return expr.as_ordered_terms(order=order)
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
class _PrintFunction:
|
| 353 |
+
"""
|
| 354 |
+
Function wrapper to replace ``**settings`` in the signature with printer defaults
|
| 355 |
+
"""
|
| 356 |
+
def __init__(self, f, print_cls: Type[Printer]):
|
| 357 |
+
# find all the non-setting arguments
|
| 358 |
+
params = list(inspect.signature(f).parameters.values())
|
| 359 |
+
assert params.pop(-1).kind == inspect.Parameter.VAR_KEYWORD
|
| 360 |
+
self.__other_params = params
|
| 361 |
+
|
| 362 |
+
self.__print_cls = print_cls
|
| 363 |
+
update_wrapper(self, f)
|
| 364 |
+
|
| 365 |
+
def __reduce__(self):
|
| 366 |
+
# Since this is used as a decorator, it replaces the original function.
|
| 367 |
+
# The default pickling will try to pickle self.__wrapped__ and fail
|
| 368 |
+
# because the wrapped function can't be retrieved by name.
|
| 369 |
+
return self.__wrapped__.__qualname__
|
| 370 |
+
|
| 371 |
+
def __call__(self, *args, **kwargs):
|
| 372 |
+
return self.__wrapped__(*args, **kwargs)
|
| 373 |
+
|
| 374 |
+
@property
|
| 375 |
+
def __signature__(self) -> inspect.Signature:
|
| 376 |
+
settings = self.__print_cls._get_initial_settings()
|
| 377 |
+
return inspect.Signature(
|
| 378 |
+
parameters=self.__other_params + [
|
| 379 |
+
inspect.Parameter(k, inspect.Parameter.KEYWORD_ONLY, default=v)
|
| 380 |
+
for k, v in settings.items()
|
| 381 |
+
],
|
| 382 |
+
return_annotation=self.__wrapped__.__annotations__.get('return', inspect.Signature.empty) # type:ignore
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def print_function(print_cls):
|
| 387 |
+
""" A decorator to replace kwargs with the printer settings in __signature__ """
|
| 388 |
+
def decorator(f):
|
| 389 |
+
if sys.version_info < (3, 9):
|
| 390 |
+
# We have to create a subclass so that `help` actually shows the docstring in older Python versions.
|
| 391 |
+
# IPython and Sphinx do not need this, only a raw Python console.
|
| 392 |
+
cls = type(f'{f.__qualname__}_PrintFunction', (_PrintFunction,), {"__doc__": f.__doc__})
|
| 393 |
+
else:
|
| 394 |
+
cls = _PrintFunction
|
| 395 |
+
return cls(f, print_cls)
|
| 396 |
+
return decorator
|
lib/python3.12/site-packages/sympy/printing/rcode.py
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
R code printer
|
| 3 |
+
|
| 4 |
+
The RCodePrinter converts single SymPy expressions into single R expressions,
|
| 5 |
+
using the functions defined in math.h where possible.
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from __future__ import annotations
|
| 12 |
+
from typing import Any
|
| 13 |
+
|
| 14 |
+
from sympy.core.numbers import equal_valued
|
| 15 |
+
from sympy.printing.codeprinter import CodePrinter
|
| 16 |
+
from sympy.printing.precedence import precedence, PRECEDENCE
|
| 17 |
+
from sympy.sets.fancysets import Range
|
| 18 |
+
|
| 19 |
+
# dictionary mapping SymPy function to (argument_conditions, C_function).
|
| 20 |
+
# Used in RCodePrinter._print_Function(self)
|
| 21 |
+
known_functions = {
|
| 22 |
+
#"Abs": [(lambda x: not x.is_integer, "fabs")],
|
| 23 |
+
"Abs": "abs",
|
| 24 |
+
"sin": "sin",
|
| 25 |
+
"cos": "cos",
|
| 26 |
+
"tan": "tan",
|
| 27 |
+
"asin": "asin",
|
| 28 |
+
"acos": "acos",
|
| 29 |
+
"atan": "atan",
|
| 30 |
+
"atan2": "atan2",
|
| 31 |
+
"exp": "exp",
|
| 32 |
+
"log": "log",
|
| 33 |
+
"erf": "erf",
|
| 34 |
+
"sinh": "sinh",
|
| 35 |
+
"cosh": "cosh",
|
| 36 |
+
"tanh": "tanh",
|
| 37 |
+
"asinh": "asinh",
|
| 38 |
+
"acosh": "acosh",
|
| 39 |
+
"atanh": "atanh",
|
| 40 |
+
"floor": "floor",
|
| 41 |
+
"ceiling": "ceiling",
|
| 42 |
+
"sign": "sign",
|
| 43 |
+
"Max": "max",
|
| 44 |
+
"Min": "min",
|
| 45 |
+
"factorial": "factorial",
|
| 46 |
+
"gamma": "gamma",
|
| 47 |
+
"digamma": "digamma",
|
| 48 |
+
"trigamma": "trigamma",
|
| 49 |
+
"beta": "beta",
|
| 50 |
+
"sqrt": "sqrt", # To enable automatic rewrite
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
# These are the core reserved words in the R language. Taken from:
|
| 54 |
+
# https://cran.r-project.org/doc/manuals/r-release/R-lang.html#Reserved-words
|
| 55 |
+
|
| 56 |
+
reserved_words = ['if',
|
| 57 |
+
'else',
|
| 58 |
+
'repeat',
|
| 59 |
+
'while',
|
| 60 |
+
'function',
|
| 61 |
+
'for',
|
| 62 |
+
'in',
|
| 63 |
+
'next',
|
| 64 |
+
'break',
|
| 65 |
+
'TRUE',
|
| 66 |
+
'FALSE',
|
| 67 |
+
'NULL',
|
| 68 |
+
'Inf',
|
| 69 |
+
'NaN',
|
| 70 |
+
'NA',
|
| 71 |
+
'NA_integer_',
|
| 72 |
+
'NA_real_',
|
| 73 |
+
'NA_complex_',
|
| 74 |
+
'NA_character_',
|
| 75 |
+
'volatile']
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class RCodePrinter(CodePrinter):
|
| 79 |
+
"""A printer to convert SymPy expressions to strings of R code"""
|
| 80 |
+
printmethod = "_rcode"
|
| 81 |
+
language = "R"
|
| 82 |
+
|
| 83 |
+
_default_settings: dict[str, Any] = dict(CodePrinter._default_settings, **{
|
| 84 |
+
'precision': 15,
|
| 85 |
+
'user_functions': {},
|
| 86 |
+
'contract': True,
|
| 87 |
+
'dereference': set(),
|
| 88 |
+
})
|
| 89 |
+
_operators = {
|
| 90 |
+
'and': '&',
|
| 91 |
+
'or': '|',
|
| 92 |
+
'not': '!',
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
_relationals: dict[str, str] = {}
|
| 96 |
+
|
| 97 |
+
def __init__(self, settings={}):
|
| 98 |
+
CodePrinter.__init__(self, settings)
|
| 99 |
+
self.known_functions = dict(known_functions)
|
| 100 |
+
userfuncs = settings.get('user_functions', {})
|
| 101 |
+
self.known_functions.update(userfuncs)
|
| 102 |
+
self._dereference = set(settings.get('dereference', []))
|
| 103 |
+
self.reserved_words = set(reserved_words)
|
| 104 |
+
|
| 105 |
+
def _rate_index_position(self, p):
|
| 106 |
+
return p*5
|
| 107 |
+
|
| 108 |
+
def _get_statement(self, codestring):
|
| 109 |
+
return "%s;" % codestring
|
| 110 |
+
|
| 111 |
+
def _get_comment(self, text):
|
| 112 |
+
return "// {}".format(text)
|
| 113 |
+
|
| 114 |
+
def _declare_number_const(self, name, value):
|
| 115 |
+
return "{} = {};".format(name, value)
|
| 116 |
+
|
| 117 |
+
def _format_code(self, lines):
|
| 118 |
+
return self.indent_code(lines)
|
| 119 |
+
|
| 120 |
+
def _traverse_matrix_indices(self, mat):
|
| 121 |
+
rows, cols = mat.shape
|
| 122 |
+
return ((i, j) for i in range(rows) for j in range(cols))
|
| 123 |
+
|
| 124 |
+
def _get_loop_opening_ending(self, indices):
|
| 125 |
+
"""Returns a tuple (open_lines, close_lines) containing lists of codelines
|
| 126 |
+
"""
|
| 127 |
+
open_lines = []
|
| 128 |
+
close_lines = []
|
| 129 |
+
loopstart = "for (%(var)s in %(start)s:%(end)s){"
|
| 130 |
+
for i in indices:
|
| 131 |
+
# R arrays start at 1 and end at dimension
|
| 132 |
+
open_lines.append(loopstart % {
|
| 133 |
+
'var': self._print(i.label),
|
| 134 |
+
'start': self._print(i.lower+1),
|
| 135 |
+
'end': self._print(i.upper + 1)})
|
| 136 |
+
close_lines.append("}")
|
| 137 |
+
return open_lines, close_lines
|
| 138 |
+
|
| 139 |
+
def _print_Pow(self, expr):
|
| 140 |
+
if "Pow" in self.known_functions:
|
| 141 |
+
return self._print_Function(expr)
|
| 142 |
+
PREC = precedence(expr)
|
| 143 |
+
if equal_valued(expr.exp, -1):
|
| 144 |
+
return '1.0/%s' % (self.parenthesize(expr.base, PREC))
|
| 145 |
+
elif equal_valued(expr.exp, 0.5):
|
| 146 |
+
return 'sqrt(%s)' % self._print(expr.base)
|
| 147 |
+
else:
|
| 148 |
+
return '%s^%s' % (self.parenthesize(expr.base, PREC),
|
| 149 |
+
self.parenthesize(expr.exp, PREC))
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def _print_Rational(self, expr):
|
| 153 |
+
p, q = int(expr.p), int(expr.q)
|
| 154 |
+
return '%d.0/%d.0' % (p, q)
|
| 155 |
+
|
| 156 |
+
def _print_Indexed(self, expr):
|
| 157 |
+
inds = [ self._print(i) for i in expr.indices ]
|
| 158 |
+
return "%s[%s]" % (self._print(expr.base.label), ", ".join(inds))
|
| 159 |
+
|
| 160 |
+
def _print_Idx(self, expr):
|
| 161 |
+
return self._print(expr.label)
|
| 162 |
+
|
| 163 |
+
def _print_Exp1(self, expr):
|
| 164 |
+
return "exp(1)"
|
| 165 |
+
|
| 166 |
+
def _print_Pi(self, expr):
|
| 167 |
+
return 'pi'
|
| 168 |
+
|
| 169 |
+
def _print_Infinity(self, expr):
|
| 170 |
+
return 'Inf'
|
| 171 |
+
|
| 172 |
+
def _print_NegativeInfinity(self, expr):
|
| 173 |
+
return '-Inf'
|
| 174 |
+
|
| 175 |
+
def _print_Assignment(self, expr):
|
| 176 |
+
from sympy.codegen.ast import Assignment
|
| 177 |
+
|
| 178 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 179 |
+
from sympy.tensor.indexed import IndexedBase
|
| 180 |
+
lhs = expr.lhs
|
| 181 |
+
rhs = expr.rhs
|
| 182 |
+
# We special case assignments that take multiple lines
|
| 183 |
+
#if isinstance(expr.rhs, Piecewise):
|
| 184 |
+
# from sympy.functions.elementary.piecewise import Piecewise
|
| 185 |
+
# # Here we modify Piecewise so each expression is now
|
| 186 |
+
# # an Assignment, and then continue on the print.
|
| 187 |
+
# expressions = []
|
| 188 |
+
# conditions = []
|
| 189 |
+
# for (e, c) in rhs.args:
|
| 190 |
+
# expressions.append(Assignment(lhs, e))
|
| 191 |
+
# conditions.append(c)
|
| 192 |
+
# temp = Piecewise(*zip(expressions, conditions))
|
| 193 |
+
# return self._print(temp)
|
| 194 |
+
#elif isinstance(lhs, MatrixSymbol):
|
| 195 |
+
if isinstance(lhs, MatrixSymbol):
|
| 196 |
+
# Here we form an Assignment for each element in the array,
|
| 197 |
+
# printing each one.
|
| 198 |
+
lines = []
|
| 199 |
+
for (i, j) in self._traverse_matrix_indices(lhs):
|
| 200 |
+
temp = Assignment(lhs[i, j], rhs[i, j])
|
| 201 |
+
code0 = self._print(temp)
|
| 202 |
+
lines.append(code0)
|
| 203 |
+
return "\n".join(lines)
|
| 204 |
+
elif self._settings["contract"] and (lhs.has(IndexedBase) or
|
| 205 |
+
rhs.has(IndexedBase)):
|
| 206 |
+
# Here we check if there is looping to be done, and if so
|
| 207 |
+
# print the required loops.
|
| 208 |
+
return self._doprint_loops(rhs, lhs)
|
| 209 |
+
else:
|
| 210 |
+
lhs_code = self._print(lhs)
|
| 211 |
+
rhs_code = self._print(rhs)
|
| 212 |
+
return self._get_statement("%s = %s" % (lhs_code, rhs_code))
|
| 213 |
+
|
| 214 |
+
def _print_Piecewise(self, expr):
|
| 215 |
+
# This method is called only for inline if constructs
|
| 216 |
+
# Top level piecewise is handled in doprint()
|
| 217 |
+
if expr.args[-1].cond == True:
|
| 218 |
+
last_line = "%s" % self._print(expr.args[-1].expr)
|
| 219 |
+
else:
|
| 220 |
+
last_line = "ifelse(%s,%s,NA)" % (self._print(expr.args[-1].cond), self._print(expr.args[-1].expr))
|
| 221 |
+
code=last_line
|
| 222 |
+
for e, c in reversed(expr.args[:-1]):
|
| 223 |
+
code= "ifelse(%s,%s," % (self._print(c), self._print(e))+code+")"
|
| 224 |
+
return(code)
|
| 225 |
+
|
| 226 |
+
def _print_ITE(self, expr):
|
| 227 |
+
from sympy.functions import Piecewise
|
| 228 |
+
return self._print(expr.rewrite(Piecewise))
|
| 229 |
+
|
| 230 |
+
def _print_MatrixElement(self, expr):
|
| 231 |
+
return "{}[{}]".format(self.parenthesize(expr.parent, PRECEDENCE["Atom"],
|
| 232 |
+
strict=True), expr.j + expr.i*expr.parent.shape[1])
|
| 233 |
+
|
| 234 |
+
def _print_Symbol(self, expr):
|
| 235 |
+
name = super()._print_Symbol(expr)
|
| 236 |
+
if expr in self._dereference:
|
| 237 |
+
return '(*{})'.format(name)
|
| 238 |
+
else:
|
| 239 |
+
return name
|
| 240 |
+
|
| 241 |
+
def _print_Relational(self, expr):
|
| 242 |
+
lhs_code = self._print(expr.lhs)
|
| 243 |
+
rhs_code = self._print(expr.rhs)
|
| 244 |
+
op = expr.rel_op
|
| 245 |
+
return "{} {} {}".format(lhs_code, op, rhs_code)
|
| 246 |
+
|
| 247 |
+
def _print_AugmentedAssignment(self, expr):
|
| 248 |
+
lhs_code = self._print(expr.lhs)
|
| 249 |
+
op = expr.op
|
| 250 |
+
rhs_code = self._print(expr.rhs)
|
| 251 |
+
return "{} {} {};".format(lhs_code, op, rhs_code)
|
| 252 |
+
|
| 253 |
+
def _print_For(self, expr):
|
| 254 |
+
target = self._print(expr.target)
|
| 255 |
+
if isinstance(expr.iterable, Range):
|
| 256 |
+
start, stop, step = expr.iterable.args
|
| 257 |
+
else:
|
| 258 |
+
raise NotImplementedError("Only iterable currently supported is Range")
|
| 259 |
+
body = self._print(expr.body)
|
| 260 |
+
return 'for({target} in seq(from={start}, to={stop}, by={step}){{\n{body}\n}}'.format(target=target, start=start,
|
| 261 |
+
stop=stop-1, step=step, body=body)
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def indent_code(self, code):
|
| 265 |
+
"""Accepts a string of code or a list of code lines"""
|
| 266 |
+
|
| 267 |
+
if isinstance(code, str):
|
| 268 |
+
code_lines = self.indent_code(code.splitlines(True))
|
| 269 |
+
return ''.join(code_lines)
|
| 270 |
+
|
| 271 |
+
tab = " "
|
| 272 |
+
inc_token = ('{', '(', '{\n', '(\n')
|
| 273 |
+
dec_token = ('}', ')')
|
| 274 |
+
|
| 275 |
+
code = [ line.lstrip(' \t') for line in code ]
|
| 276 |
+
|
| 277 |
+
increase = [ int(any(map(line.endswith, inc_token))) for line in code ]
|
| 278 |
+
decrease = [ int(any(map(line.startswith, dec_token)))
|
| 279 |
+
for line in code ]
|
| 280 |
+
|
| 281 |
+
pretty = []
|
| 282 |
+
level = 0
|
| 283 |
+
for n, line in enumerate(code):
|
| 284 |
+
if line in ('', '\n'):
|
| 285 |
+
pretty.append(line)
|
| 286 |
+
continue
|
| 287 |
+
level -= decrease[n]
|
| 288 |
+
pretty.append("%s%s" % (tab*level, line))
|
| 289 |
+
level += increase[n]
|
| 290 |
+
return pretty
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def rcode(expr, assign_to=None, **settings):
|
| 294 |
+
"""Converts an expr to a string of r code
|
| 295 |
+
|
| 296 |
+
Parameters
|
| 297 |
+
==========
|
| 298 |
+
|
| 299 |
+
expr : Expr
|
| 300 |
+
A SymPy expression to be converted.
|
| 301 |
+
assign_to : optional
|
| 302 |
+
When given, the argument is used as the name of the variable to which
|
| 303 |
+
the expression is assigned. Can be a string, ``Symbol``,
|
| 304 |
+
``MatrixSymbol``, or ``Indexed`` type. This is helpful in case of
|
| 305 |
+
line-wrapping, or for expressions that generate multi-line statements.
|
| 306 |
+
precision : integer, optional
|
| 307 |
+
The precision for numbers such as pi [default=15].
|
| 308 |
+
user_functions : dict, optional
|
| 309 |
+
A dictionary where the keys are string representations of either
|
| 310 |
+
``FunctionClass`` or ``UndefinedFunction`` instances and the values
|
| 311 |
+
are their desired R string representations. Alternatively, the
|
| 312 |
+
dictionary value can be a list of tuples i.e. [(argument_test,
|
| 313 |
+
rfunction_string)] or [(argument_test, rfunction_formater)]. See below
|
| 314 |
+
for examples.
|
| 315 |
+
human : bool, optional
|
| 316 |
+
If True, the result is a single string that may contain some constant
|
| 317 |
+
declarations for the number symbols. If False, the same information is
|
| 318 |
+
returned in a tuple of (symbols_to_declare, not_supported_functions,
|
| 319 |
+
code_text). [default=True].
|
| 320 |
+
contract: bool, optional
|
| 321 |
+
If True, ``Indexed`` instances are assumed to obey tensor contraction
|
| 322 |
+
rules and the corresponding nested loops over indices are generated.
|
| 323 |
+
Setting contract=False will not generate loops, instead the user is
|
| 324 |
+
responsible to provide values for the indices in the code.
|
| 325 |
+
[default=True].
|
| 326 |
+
|
| 327 |
+
Examples
|
| 328 |
+
========
|
| 329 |
+
|
| 330 |
+
>>> from sympy import rcode, symbols, Rational, sin, ceiling, Abs, Function
|
| 331 |
+
>>> x, tau = symbols("x, tau")
|
| 332 |
+
>>> rcode((2*tau)**Rational(7, 2))
|
| 333 |
+
'8*sqrt(2)*tau^(7.0/2.0)'
|
| 334 |
+
>>> rcode(sin(x), assign_to="s")
|
| 335 |
+
's = sin(x);'
|
| 336 |
+
|
| 337 |
+
Simple custom printing can be defined for certain types by passing a
|
| 338 |
+
dictionary of {"type" : "function"} to the ``user_functions`` kwarg.
|
| 339 |
+
Alternatively, the dictionary value can be a list of tuples i.e.
|
| 340 |
+
[(argument_test, cfunction_string)].
|
| 341 |
+
|
| 342 |
+
>>> custom_functions = {
|
| 343 |
+
... "ceiling": "CEIL",
|
| 344 |
+
... "Abs": [(lambda x: not x.is_integer, "fabs"),
|
| 345 |
+
... (lambda x: x.is_integer, "ABS")],
|
| 346 |
+
... "func": "f"
|
| 347 |
+
... }
|
| 348 |
+
>>> func = Function('func')
|
| 349 |
+
>>> rcode(func(Abs(x) + ceiling(x)), user_functions=custom_functions)
|
| 350 |
+
'f(fabs(x) + CEIL(x))'
|
| 351 |
+
|
| 352 |
+
or if the R-function takes a subset of the original arguments:
|
| 353 |
+
|
| 354 |
+
>>> rcode(2**x + 3**x, user_functions={'Pow': [
|
| 355 |
+
... (lambda b, e: b == 2, lambda b, e: 'exp2(%s)' % e),
|
| 356 |
+
... (lambda b, e: b != 2, 'pow')]})
|
| 357 |
+
'exp2(x) + pow(3, x)'
|
| 358 |
+
|
| 359 |
+
``Piecewise`` expressions are converted into conditionals. If an
|
| 360 |
+
``assign_to`` variable is provided an if statement is created, otherwise
|
| 361 |
+
the ternary operator is used. Note that if the ``Piecewise`` lacks a
|
| 362 |
+
default term, represented by ``(expr, True)`` then an error will be thrown.
|
| 363 |
+
This is to prevent generating an expression that may not evaluate to
|
| 364 |
+
anything.
|
| 365 |
+
|
| 366 |
+
>>> from sympy import Piecewise
|
| 367 |
+
>>> expr = Piecewise((x + 1, x > 0), (x, True))
|
| 368 |
+
>>> print(rcode(expr, assign_to=tau))
|
| 369 |
+
tau = ifelse(x > 0,x + 1,x);
|
| 370 |
+
|
| 371 |
+
Support for loops is provided through ``Indexed`` types. With
|
| 372 |
+
``contract=True`` these expressions will be turned into loops, whereas
|
| 373 |
+
``contract=False`` will just print the assignment expression that should be
|
| 374 |
+
looped over:
|
| 375 |
+
|
| 376 |
+
>>> from sympy import Eq, IndexedBase, Idx
|
| 377 |
+
>>> len_y = 5
|
| 378 |
+
>>> y = IndexedBase('y', shape=(len_y,))
|
| 379 |
+
>>> t = IndexedBase('t', shape=(len_y,))
|
| 380 |
+
>>> Dy = IndexedBase('Dy', shape=(len_y-1,))
|
| 381 |
+
>>> i = Idx('i', len_y-1)
|
| 382 |
+
>>> e=Eq(Dy[i], (y[i+1]-y[i])/(t[i+1]-t[i]))
|
| 383 |
+
>>> rcode(e.rhs, assign_to=e.lhs, contract=False)
|
| 384 |
+
'Dy[i] = (y[i + 1] - y[i])/(t[i + 1] - t[i]);'
|
| 385 |
+
|
| 386 |
+
Matrices are also supported, but a ``MatrixSymbol`` of the same dimensions
|
| 387 |
+
must be provided to ``assign_to``. Note that any expression that can be
|
| 388 |
+
generated normally can also exist inside a Matrix:
|
| 389 |
+
|
| 390 |
+
>>> from sympy import Matrix, MatrixSymbol
|
| 391 |
+
>>> mat = Matrix([x**2, Piecewise((x + 1, x > 0), (x, True)), sin(x)])
|
| 392 |
+
>>> A = MatrixSymbol('A', 3, 1)
|
| 393 |
+
>>> print(rcode(mat, A))
|
| 394 |
+
A[0] = x^2;
|
| 395 |
+
A[1] = ifelse(x > 0,x + 1,x);
|
| 396 |
+
A[2] = sin(x);
|
| 397 |
+
|
| 398 |
+
"""
|
| 399 |
+
|
| 400 |
+
return RCodePrinter(settings).doprint(expr, assign_to)
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def print_rcode(expr, **settings):
|
| 404 |
+
"""Prints R representation of the given expression."""
|
| 405 |
+
print(rcode(expr, **settings))
|