From cef946979d271ffbb844ea48ff6fcb9877cd7a68 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 5 Feb 2021 08:03:10 -0300 Subject: [PATCH 01/12] Adding an option for use asy to build svg graphics --- mathics/builtin/graphics.py | 58 ++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index f6fa3165d8..4cb63d1a69 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3142,7 +3142,7 @@ def get_range(min, max): return elements, calc_dimensions - def boxes_to_tex(self, leaves, **options): + def boxes_to_tex(self, leaves, forxml=False, **options): elements, calc_dimensions = self._prepare_elements( leaves, options, max_width=450 ) @@ -3176,14 +3176,12 @@ def boxes_to_tex(self, leaves, **options): asy_background = "" tex = r""" -\begin{asy} usepackage("amsmath"); size(%scm, %scm); %s %s clip(%s); %s -\end{asy} """ % ( asy_number(width / 60), asy_number(height / 60), @@ -3193,9 +3191,61 @@ def boxes_to_tex(self, leaves, **options): asy_completely_visible, ) - return tex + if forxml: + return (tex, width, height) + else: + return "\\begin{asy}\n" + tex + "\n\\end{asy}" def boxes_to_xml(self, leaves, **options): + evaluation = options.get("evaluation", None) + check_asy = False + if evaluation: + check_asy = evaluation.definitions.get_ownvalue("Settings`UseAsyForGraphics2D") + if check_asy: + check_asy = check_asy.replace.to_python() + if check_asy: + import os + from subprocess import DEVNULL, STDOUT, check_call + import tempfile + try: + check_call(['asy', '--version'], stdout=DEVNULL, stderr=DEVNULL) + except: + check_asy = False + + if check_asy: + asy, width, height = self.boxes_to_tex(leaves, forxml=True, **options) + + fin = os.path.join(tempfile._get_default_tempdir(), next(tempfile._get_candidate_names())) + fout = fin + ".svg" + with open(fin, 'w+') as borrador: + borrador.write(asy) + + try: + check_call(['asy', '-f', 'svg', '-o', fout, fin], stdout=DEVNULL, stderr=DEVNULL) + except: + print("Asy failed to build a svg") + check_asy = False + + if check_asy: + with open(fout, 'rt') as ff: + svg = ff.read() + + svg = svg[svg.find("' + % ( + int(1.5 * width), + int(1.5 * height), + base64.b64encode(svg.encode("utf8")).decode("utf8"), + ) + ) + else: + print("Asy not available. Continue with standard") + + + + elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True) xmin, xmax, ymin, ymax, w, h, width, height = calc_dimensions() From 18d9c3d91d2e69d876acc33d6349255fcebae24d Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 5 Feb 2021 08:24:54 -0300 Subject: [PATCH 02/12] sending img with b64 encoded svg instead of plane svg --- mathics/builtin/graphics.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index 4cb63d1a69..1f03487dff 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3231,7 +3231,15 @@ def boxes_to_xml(self, leaves, **options): svg = ff.read() svg = svg[svg.find("' + % ( + int(1.5 * width), + int(1.5 * height), + base64.b64encode(svg.encode("utf8")).decode("utf8"), + ) + ) + return ( '' % ( From 6b52d2684fd49188494851ccdf063689ba44f2c6 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 5 Feb 2021 08:39:10 -0300 Subject: [PATCH 03/12] Fixing boxes_to_tex return string to match with tests. --- mathics/builtin/graphics.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index 1f03487dff..9b4cb22472 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3194,7 +3194,7 @@ def boxes_to_tex(self, leaves, forxml=False, **options): if forxml: return (tex, width, height) else: - return "\\begin{asy}\n" + tex + "\n\\end{asy}" + return "\n\\begin{asy}\n" + tex + "\n\\end{asy}" def boxes_to_xml(self, leaves, **options): evaluation = options.get("evaluation", None) @@ -3234,20 +3234,20 @@ def boxes_to_xml(self, leaves, **options): return ( '' % ( - int(1.5 * width), - int(1.5 * height), + int(width), + int(height), base64.b64encode(svg.encode("utf8")).decode("utf8"), ) ) - return ( - '' - % ( - int(1.5 * width), - int(1.5 * height), - base64.b64encode(svg.encode("utf8")).decode("utf8"), - ) - ) + # return ( + # '' + # % ( + # int(width), + # int(height), + # base64.b64encode(svg.encode("utf8")).decode("utf8"), + # ) + #) else: print("Asy not available. Continue with standard") From 3eaa4caf11636601e2ec44230ba54fb13794596d Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 5 Feb 2021 08:40:16 -0300 Subject: [PATCH 04/12] clean --- mathics/builtin/graphics.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index 9b4cb22472..2e0867e467 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3239,21 +3239,9 @@ def boxes_to_xml(self, leaves, **options): base64.b64encode(svg.encode("utf8")).decode("utf8"), ) ) - - # return ( - # '' - # % ( - # int(width), - # int(height), - # base64.b64encode(svg.encode("utf8")).decode("utf8"), - # ) - #) else: print("Asy not available. Continue with standard") - - - elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True) xmin, xmax, ymin, ymax, w, h, width, height = calc_dimensions() From 4edae899d1ca9df4b70d342566ce76e4009aa047 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 5 Feb 2021 13:22:10 -0300 Subject: [PATCH 05/12] small fixes to PossibleZeroQ --- mathics/builtin/arithmetic.py | 7 +++++-- mathics/core/expression.py | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py index 0c7b055476..ec26005500 100644 --- a/mathics/builtin/arithmetic.py +++ b/mathics/builtin/arithmetic.py @@ -1343,9 +1343,13 @@ class PossibleZeroQ(SympyFunction): def apply(self, expr, evaluation): "%(name)s[expr_]" from sympy.matrices.utilities import _iszero - sympy_expr = expr.to_sympy() result = _iszero(sympy_expr) + if result is None: + # try expanding the expression + exprexp = Expression("ExpandAll", expr).evaluate(evaluation) + exprexp = exprexp.to_sympy() + result = _iszero(exprexp) if result is None: # Can't get exact answer, so try approximate equal numeric_val = Expression("N", expr).evaluate(evaluation) @@ -1359,7 +1363,6 @@ def apply(self, expr, evaluation): if Expression("Simplify", expr).evaluate(evaluation) == Integer(0) else SymbolFalse ) - return from_python(result) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index 263690d680..795470ce60 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -2109,7 +2109,7 @@ def __neg__(self) -> 'Rational': @property def is_zero(self) -> bool: - return self.numerator().is_zero + return self.numerator().is_zero and not(self.denominator().is_zero) class Real(Number): @@ -2259,7 +2259,8 @@ def is_zero(self) -> bool: def is_approx_zero(self) -> bool: # FIXME: figure out how to hook int $MachinePrecision and # what the right definition here would be. - return abs(self.value) <= 10**-14 + res = abs(self.value) <= 1e-14 + return res class PrecisionReal(Real): From 7857d4260a02eddc3eb59317c725f0da73cec553 Mon Sep 17 00:00:00 2001 From: mmatera Date: Fri, 5 Feb 2021 13:39:47 -0300 Subject: [PATCH 06/12] more PossibleZeroQ fixes --- mathics/core/expression.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mathics/core/expression.py b/mathics/core/expression.py index 795470ce60..063b1aaff1 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -2257,9 +2257,9 @@ def is_zero(self) -> bool: @property def is_approx_zero(self) -> bool: - # FIXME: figure out how to hook int $MachinePrecision and - # what the right definition here would be. - res = abs(self.value) <= 1e-14 + # In WMA, Chop[10.^(-10)] == 0, + # so, lets take it. + res = abs(self.value) <= 1e-10 return res From 12fea1917fd30468ff5e2d09adf754df45f15674 Mon Sep 17 00:00:00 2001 From: mmatera Date: Sat, 6 Feb 2021 11:52:05 -0300 Subject: [PATCH 07/12] Fix asy output for compabitibility with the doc generator. In , catch possible fail in os.getlogin() --- mathics/builtin/graphics.py | 2 +- mathics/builtin/system.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index 2e0867e467..f3a37a2215 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3194,7 +3194,7 @@ def boxes_to_tex(self, leaves, forxml=False, **options): if forxml: return (tex, width, height) else: - return "\n\\begin{asy}\n" + tex + "\n\\end{asy}" + return "\n\\begin{asy}\n" + tex + "\n\\end{asy}\n" def boxes_to_xml(self, leaves, **options): evaluation = options.get("evaluation", None) diff --git a/mathics/builtin/system.py b/mathics/builtin/system.py index 2dba9673d7..cb2c0cdf15 100644 --- a/mathics/builtin/system.py +++ b/mathics/builtin/system.py @@ -406,8 +406,16 @@ class UserName(Predefined): name = "$UserName" + messages = { + 'nologin': "UserName not available in this system.", + } + def evaluate(self, evaluation) -> String: - return String(os.getlogin()) + try: + return String(os.getlogin()) + except: + evaluation.message('$UserName', 'nologin') + return class Version(Predefined): From b896993d80ed79a2c7863d6ea6f92e847acf9c0f Mon Sep 17 00:00:00 2001 From: mmatera Date: Sat, 6 Feb 2021 12:01:09 -0300 Subject: [PATCH 08/12] Add changes to the Changes list --- CHANGES.rst | 7 +++++-- mathics/builtin/graphics.py | 7 +++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 55d6e29b2b..0cca7f66eb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -25,9 +25,12 @@ Document updates Enhancements and Bug fixes: +++++++++++++++++++++++++++ -- Fix evaluation timeouts +- Fix evaluation timeouts. - ``Sum``'s lower and upper bounds values can now be Mathics expressions - +- Partial fix for MathML 2D Graphics. Requires the installation of the package Asymptote, and to set + the variable ``Settings`UseAsyForGraphics2D`` to ``System`True``. +- Set the numeric threshold in PossibleZeroQ to 10^(-10), for compatibility with ``System`Chop``. +- Catch possible exceptions generated by ``$UserName``. 1.1.1 ----- diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index f3a37a2215..840e210d99 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3202,7 +3202,7 @@ def boxes_to_xml(self, leaves, **options): if evaluation: check_asy = evaluation.definitions.get_ownvalue("Settings`UseAsyForGraphics2D") if check_asy: - check_asy = check_asy.replace.to_python() + check_asy = check_asy.replace.is_true() if check_asy: import os from subprocess import DEVNULL, STDOUT, check_call @@ -3214,12 +3214,11 @@ def boxes_to_xml(self, leaves, **options): if check_asy: asy, width, height = self.boxes_to_tex(leaves, forxml=True, **options) - fin = os.path.join(tempfile._get_default_tempdir(), next(tempfile._get_candidate_names())) fout = fin + ".svg" with open(fin, 'w+') as borrador: borrador.write(asy) - + try: check_call(['asy', '-f', 'svg', '-o', fout, fin], stdout=DEVNULL, stderr=DEVNULL) except: @@ -3241,7 +3240,7 @@ def boxes_to_xml(self, leaves, **options): ) else: print("Asy not available. Continue with standard") - + elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True) xmin, xmax, ymin, ymax, w, h, width, height = calc_dimensions() From b97bbb96d16ef491ee71d69435efaaf7949325f3 Mon Sep 17 00:00:00 2001 From: mmatera Date: Sat, 6 Feb 2021 15:43:03 -0300 Subject: [PATCH 09/12] fix density plots --- mathics/builtin/graphics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index 840e210d99..cc54210c64 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3220,7 +3220,7 @@ def boxes_to_xml(self, leaves, **options): borrador.write(asy) try: - check_call(['asy', '-f', 'svg', '-o', fout, fin], stdout=DEVNULL, stderr=DEVNULL) + check_call(['asy', '-f', 'svg', '--svgemulation' ,'-o', fout, fin], stdout=DEVNULL, stderr=DEVNULL) except: print("Asy failed to build a svg") check_asy = False From 08d4b631c9d95a3aaf38db62b11438568a5bff6f Mon Sep 17 00:00:00 2001 From: mmatera Date: Sun, 21 Feb 2021 18:28:43 -0300 Subject: [PATCH 10/12] cleanup --- mathics/builtin/arithmetic.py | 4 +++- mathics/builtin/system.py | 4 ---- mathics/core/expression.py | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mathics/builtin/arithmetic.py b/mathics/builtin/arithmetic.py index ec26005500..611c17adf6 100644 --- a/mathics/builtin/arithmetic.py +++ b/mathics/builtin/arithmetic.py @@ -1343,13 +1343,14 @@ class PossibleZeroQ(SympyFunction): def apply(self, expr, evaluation): "%(name)s[expr_]" from sympy.matrices.utilities import _iszero + sympy_expr = expr.to_sympy() result = _iszero(sympy_expr) if result is None: # try expanding the expression exprexp = Expression("ExpandAll", expr).evaluate(evaluation) exprexp = exprexp.to_sympy() - result = _iszero(exprexp) + result = _iszero(exprexp) if result is None: # Can't get exact answer, so try approximate equal numeric_val = Expression("N", expr).evaluate(evaluation) @@ -1363,6 +1364,7 @@ def apply(self, expr, evaluation): if Expression("Simplify", expr).evaluate(evaluation) == Integer(0) else SymbolFalse ) + return from_python(result) diff --git a/mathics/builtin/system.py b/mathics/builtin/system.py index ed825568ca..71c8b926ef 100644 --- a/mathics/builtin/system.py +++ b/mathics/builtin/system.py @@ -406,10 +406,6 @@ class UserName(Predefined): name = "$UserName" - messages = { - 'nologin': "UserName not available in this system.", - } - def evaluate(self, evaluation) -> String: try: user = os.getlogin() diff --git a/mathics/core/expression.py b/mathics/core/expression.py index 0ebd511c0d..56d62ffb99 100644 --- a/mathics/core/expression.py +++ b/mathics/core/expression.py @@ -2137,7 +2137,7 @@ def __neg__(self) -> 'Rational': @property def is_zero(self) -> bool: - return self.numerator().is_zero and not(self.denominator().is_zero) + return self.numerator().is_zero and not self.denominator().is_zero() class Real(Number): From 4b4ddb39d82f222cbc06b886473a0abd4c61c63a Mon Sep 17 00:00:00 2001 From: mmatera Date: Sun, 21 Feb 2021 18:32:56 -0300 Subject: [PATCH 11/12] hide forxml parameter --- mathics/builtin/graphics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index cc54210c64..aa11359032 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -3142,7 +3142,7 @@ def get_range(min, max): return elements, calc_dimensions - def boxes_to_tex(self, leaves, forxml=False, **options): + def boxes_to_tex(self, leaves, **options): elements, calc_dimensions = self._prepare_elements( leaves, options, max_width=450 ) @@ -3190,7 +3190,7 @@ def boxes_to_tex(self, leaves, forxml=False, **options): asy_box, asy_completely_visible, ) - + forxml = options.get("forxml", None) if forxml: return (tex, width, height) else: From 108dfc3f1c21c533956f1f38b514b676b43a0e86 Mon Sep 17 00:00:00 2001 From: mmatera Date: Sun, 21 Feb 2021 20:04:47 -0300 Subject: [PATCH 12/12] cleanup. Adding warning messages --- mathics/builtin/graphics.py | 50 ++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index aa11359032..ddb66a865d 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -26,6 +26,8 @@ Real, String, Symbol, + SymbolTrue, + SymbolFalse, strip_context, system_symbols, system_symbols_dict, @@ -2936,6 +2938,12 @@ class GraphicsBox(BoxConstruct): attributes = ("HoldAll", "ReadProtected") + messages = { + "asynotav": 'Asymptote is not available in this system. Using the buggy backend.', + "noasyfile": 'Asy requires write permisions over a temporary file, but it was not available. Using the buggy backend', + "asyfail": 'Asymptote failed building the svg picture. Using the buggy backend.', + } + def boxes_to_text(self, leaves, **options): self._prepare_elements(leaves, options) # to test for Box errors return "-Graphics-" @@ -3203,6 +3211,7 @@ def boxes_to_xml(self, leaves, **options): check_asy = evaluation.definitions.get_ownvalue("Settings`UseAsyForGraphics2D") if check_asy: check_asy = check_asy.replace.is_true() + if check_asy: import os from subprocess import DEVNULL, STDOUT, check_call @@ -3211,36 +3220,41 @@ def boxes_to_xml(self, leaves, **options): check_call(['asy', '--version'], stdout=DEVNULL, stderr=DEVNULL) except: check_asy = False + evaluation.message("GraphicsBox", "asynotav") + Expression("Set", Symbol("Settings`UseAsyForGraphics2D"), SymbolFalse).evaluate(evaluation) if check_asy: asy, width, height = self.boxes_to_tex(leaves, forxml=True, **options) fin = os.path.join(tempfile._get_default_tempdir(), next(tempfile._get_candidate_names())) fout = fin + ".svg" - with open(fin, 'w+') as borrador: - borrador.write(asy) + try: + with open(fin, 'w+') as borrador: + borrador.write(asy) + except: + evaluation.message("GraphicsBox", "noasyfile") + check_asy = False + if check_asy: try: check_call(['asy', '-f', 'svg', '--svgemulation' ,'-o', fout, fin], stdout=DEVNULL, stderr=DEVNULL) except: - print("Asy failed to build a svg") + evaluation.message("GraphicsBox", "asyfail") check_asy = False - if check_asy: - with open(fout, 'rt') as ff: - svg = ff.read() - - svg = svg[svg.find("' - % ( - int(width), - int(height), - base64.b64encode(svg.encode("utf8")).decode("utf8"), - ) + if check_asy: + with open(fout, 'rt') as ff: + svg = ff.read() + + svg = svg[svg.find("' + % ( + int(width), + int(height), + base64.b64encode(svg.encode("utf8")).decode("utf8"), ) - else: - print("Asy not available. Continue with standard") - + ) + # Not using asymptote. Continue with the buggy backend... elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True) xmin, xmax, ymin, ymax, w, h, width, height = calc_dimensions()