diff --git a/mathics/builtin/__init__.py b/mathics/builtin/__init__.py index 155e8ee11d..f57406299e 100755 --- a/mathics/builtin/__init__.py +++ b/mathics/builtin/__init__.py @@ -130,7 +130,7 @@ def is_builtin(var): _builtins = [] builtins_by_module = {} -for subdir in ("specialfns",): +for subdir in ("drawing", "numbers", "specialfns",): import_name = f"{__name__}.{subdir}" builtin_module = importlib.import_module(import_name) submodule_names = [ diff --git a/mathics/builtin/assignment.py b/mathics/builtin/assignment.py index c50be16328..8a0ebda339 100644 --- a/mathics/builtin/assignment.py +++ b/mathics/builtin/assignment.py @@ -1922,11 +1922,11 @@ class LoadModule(Builtin): def apply(self, module, evaluation): "LoadModule[module_String]" try: - module_loaded = evaluation.definitions.load_pymathics_module(module.value) - except PyMathicsLoadException as e: + evaluation.definitions.load_pymathics_module(module.value) + except PyMathicsLoadException: evaluation.message(self.name, "notmathicslib", module) return SymbolFailed - except ImportError as e: + except ImportError: evaluation.message(self.get_name(), "notfound", module) return SymbolFailed else: diff --git a/mathics/builtin/comparison.py b/mathics/builtin/comparison.py index 04a3aa9933..e083bf48fb 100644 --- a/mathics/builtin/comparison.py +++ b/mathics/builtin/comparison.py @@ -13,7 +13,7 @@ SympyFunction, ) -from mathics.builtin.constants import mp_convert_constant +from mathics.builtin.numbers.constants import mp_convert_constant from mathics.core.expression import ( COMPARE_PREC, diff --git a/mathics/builtin/drawing/__init__.py b/mathics/builtin/drawing/__init__.py new file mode 100644 index 0000000000..bbbc3fd78b --- /dev/null +++ b/mathics/builtin/drawing/__init__.py @@ -0,0 +1,3 @@ +""" +Graphics, Drawing, and Images +""" diff --git a/mathics/builtin/colors.py b/mathics/builtin/drawing/colors.py similarity index 100% rename from mathics/builtin/colors.py rename to mathics/builtin/drawing/colors.py diff --git a/mathics/builtin/graphics3d.py b/mathics/builtin/drawing/graphics3d.py similarity index 99% rename from mathics/builtin/graphics3d.py rename to mathics/builtin/drawing/graphics3d.py index 59527a76d8..f7e9df4477 100644 --- a/mathics/builtin/graphics3d.py +++ b/mathics/builtin/drawing/graphics3d.py @@ -14,7 +14,7 @@ SymbolList, ) from mathics.builtin.base import BoxConstructError, Builtin, InstanceableBuiltin -from .graphics import ( +from mathics.builtin.graphics import ( Graphics, GraphicsBox, PolygonBox, diff --git a/mathics/builtin/image.py b/mathics/builtin/drawing/image.py similarity index 99% rename from mathics/builtin/image.py rename to mathics/builtin/drawing/image.py index db5f3a3e27..861156c409 100644 --- a/mathics/builtin/image.py +++ b/mathics/builtin/drawing/image.py @@ -21,7 +21,7 @@ SymbolRule, from_python, ) -from mathics.builtin.colors import ( +from mathics.builtin.drawing.colors import ( convert as convert_color, colorspaces as known_colorspaces, ) @@ -1599,7 +1599,7 @@ def apply(self, values, evaluation, options): ): color_function = String("LakeColors") - from mathics.builtin.plot import gradient_palette + from mathics.builtin.drawing.plot import gradient_palette cmap = gradient_palette(color_function, n, evaluation) if not cmap: diff --git a/mathics/builtin/plot.py b/mathics/builtin/drawing/plot.py similarity index 99% rename from mathics/builtin/plot.py rename to mathics/builtin/drawing/plot.py index 7167cbdcfe..65be21f606 100644 --- a/mathics/builtin/plot.py +++ b/mathics/builtin/drawing/plot.py @@ -24,12 +24,13 @@ SymbolN, SymbolRule, ) + from mathics.builtin.base import Builtin -from mathics.builtin.scoping import dynamic_scoping -from mathics.builtin.options import options_to_rules -from mathics.builtin.numeric import chop from mathics.builtin.graphics import Graphics -from mathics.builtin.graphics3d import Graphics3D +from mathics.builtin.drawing.graphics3d import Graphics3D +from mathics.builtin.numeric import chop +from mathics.builtin.options import options_to_rules +from mathics.builtin.scoping import dynamic_scoping try: @@ -39,7 +40,6 @@ except ImportError: has_compile = False - def gradient_palette(color_function, n, evaluation): # always returns RGB values if isinstance(color_function, String): color_data = Expression("ColorData", color_function).evaluate(evaluation) @@ -340,7 +340,6 @@ def get_plot_range(values, all_values, option): class _Plot(Builtin): - from .graphics import Graphics attributes = ("HoldAll",) @@ -705,8 +704,6 @@ def find_excl(excl): class _Chart(Builtin): attributes = ("HoldAll",) - from .graphics import Graphics - options = Graphics.options.copy() options.update( { @@ -1135,8 +1132,6 @@ class Histogram(Builtin): = -Graphics- """ - from .graphics import Graphics - attributes = ("HoldAll",) options = Graphics.options.copy() @@ -2224,8 +2219,6 @@ class ListPlot(_ListPlot): = -Graphics- """ - from .graphics import Graphics - attributes = ("HoldAll",) options = Graphics.options.copy() @@ -2264,8 +2257,6 @@ class ListLinePlot(_ListPlot): = -Graphics- """ - from .graphics import Graphics - attributes = ("HoldAll",) options = Graphics.options.copy() @@ -2344,8 +2335,6 @@ class Plot3D(_Plot3D): #> Plot3D[x + 2y, {x, -2, 2}, {y, -2, 2}] // TeXForm """ - from .graphics import Graphics - attributes = ("HoldAll",) options = Graphics.options.copy() @@ -2428,8 +2417,6 @@ class DensityPlot(_Plot3D): = -Graphics- """ - from .graphics import Graphics - attributes = ("HoldAll",) options = Graphics.options.copy() diff --git a/mathics/builtin/rgbcolor.py b/mathics/builtin/drawing/rgbcolor.py similarity index 100% rename from mathics/builtin/rgbcolor.py rename to mathics/builtin/drawing/rgbcolor.py diff --git a/mathics/builtin/files.py b/mathics/builtin/files.py index fd873865e6..7f6919e854 100644 --- a/mathics/builtin/files.py +++ b/mathics/builtin/files.py @@ -35,7 +35,6 @@ String, Symbol, SymbolFailed, - SymbolFalse, SymbolNull, SymbolTrue, from_mpmath, @@ -46,10 +45,8 @@ Stream, path_search, stream_manager, - urlsave_tmp, ) from mathics.builtin.base import Builtin, Predefined, BinaryOperator, PrefixOperator -from mathics.builtin.numeric import Hash from mathics.builtin.strings import to_python_encoding from mathics.builtin.base import MessageException diff --git a/mathics/builtin/filesystem.py b/mathics/builtin/filesystem.py index 8a90b95397..b01472e478 100644 --- a/mathics/builtin/filesystem.py +++ b/mathics/builtin/filesystem.py @@ -15,7 +15,6 @@ from mathics.version import __version__ # noqa used in loading to check consistency. from mathics.core.expression import ( - BaseExpression, Expression, String, Symbol, @@ -38,7 +37,11 @@ ) from mathics.builtin.base import Builtin, Predefined -from mathics.builtin.files import DIRECTORY_STACK, INITIAL_DIR, mathics_open +from mathics.builtin.files import ( + DIRECTORY_STACK, + INITIAL_DIR, # noqa is used via global + mathics_open + ) from mathics.builtin.numeric import Hash from mathics.builtin.strings import to_regex from mathics.builtin.base import MessageException diff --git a/mathics/builtin/graphics.py b/mathics/builtin/graphics.py index 6f146ad437..6e26b51ee5 100644 --- a/mathics/builtin/graphics.py +++ b/mathics/builtin/graphics.py @@ -29,12 +29,11 @@ SymbolList, SymbolN, SymbolMakeBoxes, - strip_context, system_symbols, system_symbols_dict, from_python, ) -from mathics.builtin.colors import convert as convert_color +from mathics.builtin.drawing.colors import convert as convert_color from mathics.core.numbers import machine_epsilon GRAPHICS_OPTIONS = { @@ -60,7 +59,7 @@ class ColorError(BoxConstructError): def get_class(name): - from mathics.builtin.graphics3d import GLOBALS3D + from mathics.builtin.drawing.graphics3d import GLOBALS3D c = GLOBALS.get(name) if c is None: @@ -119,7 +118,9 @@ def cut(value): return value -def create_css(edge_color=None, face_color=None, stroke_width=None, font_color=None, opacity=1.0): +def create_css( + edge_color=None, face_color=None, stroke_width=None, font_color=None, opacity=1.0 +): css = [] if edge_color is not None: color, stroke_opacity = edge_color.to_css() @@ -524,7 +525,7 @@ def convert(content): options[option] = Expression(SymbolN, options[option]).evaluate( evaluation ) - from mathics.builtin.graphics3d import Graphics3DBox, Graphics3D + from mathics.builtin.drawing.graphics3d import Graphics3DBox, Graphics3D if type(self) is Graphics: return GraphicsBox( @@ -1004,7 +1005,7 @@ def distance(a, b): else: return Expression( "List", - *[distance(a, b) for a, b in zip(c1.leaves, c2.leaves)] + *[distance(a, b) for a, b in zip(c1.leaves, c2.leaves)], ) else: return Expression(SymbolList, *[distance(c, c2) for c in c1.leaves]) @@ -1805,7 +1806,7 @@ def parse_component(segments): k = spline_degree.get_int_value() elif head == "System`BSplineCurve": raise NotImplementedError # FIXME convert bspline to bezier here - parts = segment.leaves + # parts = segment.leaves else: raise BoxConstructError @@ -2569,7 +2570,16 @@ def default_arrow(px, py, vx, vy, t1, s): class InsetBox(_GraphicsElement): - def init(self, graphics, style, item=None, content=None, pos=None, opos=(0, 0), opacity=1.0): + def init( + self, + graphics, + style, + item=None, + content=None, + pos=None, + opos=(0, 0), + opacity=1.0, + ): super(InsetBox, self).init(graphics, item, style) self.color = self.style.get_option("System`FontColor") @@ -2618,15 +2628,16 @@ def to_svg(self, offset=None): svg = "\n" + content + "\n" else: css_style = create_css( - font_color=self.color, edge_color=self.color, face_color=self.color, opacity=self.opacity + font_color=self.color, + edge_color=self.color, + face_color=self.color, + opacity=self.opacity, ) text_pos_opts = f'x="{x}" y="{y}" ox="{self.opos[0]}" oy="{self.opos[1]}"' # FIXME: don't hard code text_style_opts, but allow these to be adjustable. text_style_opts = "text-anchor:middle; dominant-baseline:middle;" content = self.content.boxes_to_text(evaluation=self.graphics.evaluation) - svg = ( - f'{content}' - ) + svg = f'{content}' # content = self.content.boxes_to_mathml(evaluation=self.graphics.evaluation) # style = create_css(font_color=self.color) diff --git a/mathics/builtin/inference.py b/mathics/builtin/inference.py index 5dda95db1c..94c296203d 100644 --- a/mathics/builtin/inference.py +++ b/mathics/builtin/inference.py @@ -1,7 +1,9 @@ +# -*- coding: utf-8 -*- + from mathics.version import __version__ # noqa used in loading to check consistency. + from mathics.core.expression import ( Expression, - Symbol, SymbolTrue, SymbolFalse, ) @@ -14,8 +16,103 @@ # TODO: Extend these rules? +def debug_logical_expr(pref, expr, evaluation): + pass + # return + # print(pref , expr) #expr.format(evaluation,"OutputForm").boxes_to_text(evaluation=evaluation)) + + +logical_algebraic_rules_spec = { + # Inequality rules + "Unequal[a_, b_]": "Not[Equal[a, b]]", + "Greater[a_, b_]": "Less[b, a]", + "GreaterEqual[a_, b_]": "Not[Less[a, b]]", + "LessEqual[a_, b_]": "Not[Less[b, a]]", + "PositiveQ[a_]": "Less[0, a]", + "NegativeQ[a_]": "Less[a, 0]", + # Logical basic reductions + "Or[q_, Not[q_]]": "True", + "Or[q_,]": "q", + "Or[q_, q_]": "q", + "Or[pred1___, q_, pred2___, q_, pred3___]": "Or[pred1, q, pred2, pred3]", + # TODO: Logical operations should sort the leaves... + "Or[Not[q_], q_]": "True", + "Or[pred1___, q_, pred2___, Not[q_], pred3___]": "Or[pred1, pred2, pred3]", + "Or[pred1___, Not[q_], pred2___, q_, pred3___]": "Or[pred1, pred2, pred3]", + "And[q_,q_]": "q", + "And[q_, Not[q_]]": "False", + "And[Not[q_],q_]": "False", + "And[pred1___, q_, pred2___, Not[q_], pred3___]": "False", + "And[pred1___, Not[q_], pred2___, q_, pred3___]": "False", + # Logical reductions involving equalities + "Or[pred1___, a_==b_, pred2___ , b_==a_, pred3___]": "Or[pred1, a==b, pred2, pred3]", + "And[pred1___, a_==b_, pred2___ , b_==a_, pred3___]": "And[pred1, a==b, pred2, pred3]", + "Or[pred1___, a_==b_, pred2___ , Not[b_==a_], pred3___]": "Or[pred1, pred2, pred3]", + "And[pred1___, a_==b_, pred2___ , Not[b_==a_], pred3___]": "False", + "Xor[q_, Not[q_]]": "True", + "Xor[a_==b_, Not[b_==a_]]": "True", + # Logical reductions involving inequalities + "Or[a_b)", + "Or[b_==a_, a_b)", + "And[a_=b", + "Not[a_==b_]": "a!=b", +} + -def get_assumptions_list(evaluation): +logical_algebraic_rules = None +remove_not_rules = None + + +def ensure_logical_algebraic_rules(): + global logical_algebraic_rules + global remove_not_rules + if logical_algebraic_rules is None: + logical_algebraic_rules = [] + for pattern, replace in logical_algebraic_rules_spec.items(): + pattern = parse_builtin_rule(pattern, SystemDefinitions()) + logical_algebraic_rules.append( + Rule(pattern, parse_builtin_rule(replace), system=True) + ) + remove_not_rules = [] + for pattern, replace in remove_not_rules_spec.items(): + pattern = parse_builtin_rule(pattern, SystemDefinitions()) + remove_not_rules.append( + Rule(pattern, parse_builtin_rule(replace), system=True) + ) + return + + +def remove_nots_when_unnecesary(pred, evaluation): + global remove_not_rules + cc = True + while cc: + pred, cc = pred.apply_rules(remove_not_rules, evaluation) + debug_logical_expr("-> ", pred, evaluation) + if pred.is_true() or pred == SymbolFalse: + return pred + return pred + + +def get_assumptions_list(evaluation): assumptions = None assumptions_def = evaluation.definitions.get_definition( "System`$Assumptions", only_if_exists=True @@ -35,23 +132,251 @@ def get_assumptions_list(evaluation): return assumptions +def remove_duplicated_assumptions(assumptions_list, evaluation): + if len(assumptions_list) == 0: + return assumptions_list + assumptions_list = sorted(assumptions_list) + unique_assumptions = [assumptions_list[0]] + for i, assumption in enumerate(assumptions_list): + if not (assumption == unique_assumptions[-1]): + unique_assumptions.append(assumption) + return unique_assumptions -def evaluate_predicate(pred, evaluation): - if pred.has_form(("List", "Sequence"), None): - return Expression(pred._head, *[evaluate_predicate(subp, evaluation) for subp in pred._leaves] ) - assumptions = get_assumptions_list(evaluation) +def logical_expand_assumptions(assumptions_list, evaluation): + new_assumptions_list = [] + changed = False + for assumption in assumptions_list: + if assumption.is_symbol(): + if assumption.is_true(): + changed = True + continue + if assumption == SymbolFalse: + evaluation.message("Assumption", "faas") + changed = True + continue + if assumption.is_numeric(): + evaluation.message("Assumption", "baas") + changed = True + continue + new_assumptions_list.append(assumption) + continue + if assumption.has_form("And", None): + changed = True + for leaf in assumption.leaves: + new_assumptions_list.append(leaf) + continue + if assumption.has_form("Not", 1): + sentence = assumption._leaves[0] + if sentence.has_form("Or", None): + changed = True + for leaf in sentence._leaves: + new_assumptions_list.append(Expression("Not", leaf)) + continue + if sentence.has_form("And", None): + leaves = (Expression("Not", leaf) for leaf in sentence._leaves) + new_assumptions_list.append(Expression("Or", *leaves)) + continue + if sentence.has_form("Implies", 2): + changed = True + new_assumptions_list.append(sentence._leaves[0]) + new_assumptions_list.append(Expression("Not", sentence._leaves[1])) + if assumption.has_form("Nor", None): + changed = True + for leaf in assumption.leaves: + new_assumptions_list.append(Expression("Not", leaf)) + continue + else: + new_assumptions_list.append(assumption) + + if changed: + new_assumptions_list = remove_duplicated_assumptions( + new_assumptions_list, evaluation + ) + + return new_assumptions_list, changed + + +def algebraic_expand_assumptions(assumptions_list, evaluation): + global logical_algebraic_rules + ensure_logical_algebraic_rules() + new_assumptions_list = [] + changed = False + # First apply standard rules of reduction. + # These rules are generated the first time that are used. + for assumption in assumptions_list: + assumption, applied = assumption.apply_rules( + logical_algebraic_rules, evaluation + ) + changed = changed or applied + new_assumptions_list.append(assumption) + if changed: + return new_assumptions_list, changed + # If not changed, let's try with the next set of rules + for assumption in assumptions_list: + if assumption.has_form("Not", 1): + nas, local_change = algebraic_expand_assumptions( + [assumption._leaves[0]], evaluation + ) + if local_change: + changed = local_change + for na in nas: + if na.has_form("Not", 1): + new_assumptions_list.append(na._leaves[0]) + else: + new_assumptions_list.append(Expression("Not", na)) + else: + new_assumptions_list.append(assumption) + elif assumption.has_form(("Equal", "Unequal", "Equivalent"), (3, None)): + leaves = assumption.leaves() + head = assumption.get_head() + changed = True + for i in range(len(leaves)): + for j in range(i): + new_assumptions_list.append(Expression(head, leaves[i], leaves[j])) + new_assumptions_list.append(Expression(head, leaves[j], leaves[i])) + elif assumption.has_form( + ("Less", "Greater", "LessEqual", "GreaterEqual"), (3, None) + ): + leaves = assumption.leaves() + head = assumption.get_head() + changed = True + for i in range(len(leaves)): + for j in range(i): + new_assumptions_list.append(Expression(head, leaves[i], leaves[j])) + else: + new_assumptions_list.append(assumption) + + if changed: + assumptions_list = remove_duplicated_assumptions( + new_assumptions_list, evaluation + ) + new_assumptions_list = [] + for assumption in assumptions_list: + assumption, applied = assumption.apply_rules( + logical_algebraic_rules, evaluation + ) + new_assumptions_list.append(assumption) + return new_assumptions_list, changed + + +def get_assumption_rules_dispatch(evaluation): + # TODO: cache the generated rules... + assumptions_list = get_assumptions_list(evaluation) + if assumptions_list is None: + return None + + # check for consistency: + consistent_assumptions = Expression("And", *assumptions_list) + val_consistent_assumptions = consistent_assumptions.evaluate(evaluation) + if val_consistent_assumptions == SymbolFalse: + evaluation.message("Inconsistent assumptions") + + # Expands Logically + assumptions_list, cont = logical_expand_assumptions(assumptions_list, evaluation) + while cont: + assumptions_list, cont = logical_expand_assumptions( + assumptions_list, evaluation + ) + + # Expands algebraically + assumptions_list, cont = algebraic_expand_assumptions(assumptions_list, evaluation) + while cont: + assumptions_list, cont = algebraic_expand_assumptions( + assumptions_list, evaluation + ) assumption_rules = [] - for assumption in assumptions: - true_state = True - while assumption.has_form("Not",1): - true_state = False - assumption = assumption._leaves[0] - if true_state: - assumption_rules.append(Rule(assumption, SymbolTrue)) + for pat in assumptions_list: + value = True + while pat.has_form("Not", 1): + value = not value + pat = pat._leaves[0] + + if value: + symbol_value = SymbolTrue + symbol_negate_value = SymbolFalse + else: + symbol_value = SymbolFalse + symbol_negate_value = SymbolTrue + + if pat.has_form("Equal", 2): + if value: + lhs, rhs = pat._leaves + if lhs.is_numeric(): + assumption_rules.append(Rule(rhs, lhs)) + else: + assumption_rules.append(Rule(lhs, rhs)) + else: + assumption_rules.append(Rule(pat, SymbolFalse)) + symm_pat = Expression(pat._head, pat._leaves[1], pat._leaves[0]) + assumption_rules.append(Rule(symm_pat, SymbolFalse)) + elif pat.has_form("Equivalent", 2): + assumption_rules.append(Rule(pat, symbol_value)) + symm_pat = Expression(pat._head, pat._leaves[1], pat._leaves[0]) + assumption_rules.append(Rule(symm_pat, symbol_value)) + elif pat.has_form("Less", 2): + if value: + assumption_rules.append(Rule(pat, SymbolTrue)) + assumption_rules.append( + Rule( + Expression(pat._head, pat._leaves[1], pat._leaves[0]), + SymbolFalse, + ) + ) + for head in ("Equal", "Equivalent"): + assumption_rules.append( + Rule( + Expression(head, pat._leaves[0], pat._leaves[1]), + SymbolFalse, + ) + ) + assumption_rules.append( + Rule( + Expression(head, pat._leaves[1], pat._leaves[0]), + SymbolFalse, + ) + ) + else: + assumption_rules.append(Rule(pat, SymbolFalse)) else: - assumption_rules.append(Rule(assumption, SymbolFalse)) - - pred, changed = pred.apply_rules(assumption_rules, evaluation) + assumption_rules.append(Rule(pat, symbol_value)) + # TODO: expand the pred and assumptions into an standard, + # atomized form, and then apply the rules... + if len(assumption_rules) == 0: + return None + return assumption_rules + + +def evaluate_predicate(pred, evaluation): + global logical_algebraic_rules + global remove_not_rules + + if pred.has_form(("List", "Sequence"), None): + return Expression( + pred._head, *[evaluate_predicate(subp, evaluation) for subp in pred._leaves] + ) + + debug_logical_expr("reducing ", pred, evaluation) + ensure_logical_algebraic_rules() pred = pred.evaluate(evaluation) + debug_logical_expr("-> ", pred, evaluation) + cc = True + while cc: + pred, cc = pred.apply_rules(logical_algebraic_rules, evaluation) + debug_logical_expr("-> ", pred, evaluation) + if pred.is_true() or pred == SymbolFalse: + return pred + + assumption_rules = get_assumption_rules_dispatch(evaluation) + if assumption_rules is None: + return remove_nots_when_unnecesary(pred, evaluation).evaluate(evaluation) + + if assumption_rules is not None: + debug_logical_expr(" Now, using the assumptions over ", pred, evaluation) + changed = True + while changed: + pred, changed = pred.apply_rules(assumption_rules, evaluation) + debug_logical_expr(" -> ", pred, evaluation) + + pred = remove_nots_when_unnecesary(pred, evaluation).evaluate(evaluation) return pred diff --git a/mathics/builtin/lists.py b/mathics/builtin/lists.py index 054dced5a9..dde8f65b9c 100644 --- a/mathics/builtin/lists.py +++ b/mathics/builtin/lists.py @@ -52,7 +52,7 @@ from mathics.core.evaluation import BreakInterrupt, ContinueInterrupt, ReturnInterrupt from mathics.core.rules import Pattern from mathics.core.convert import from_sympy -from mathics.builtin.algebra import cancel +from mathics.builtin.numbers.algebra import cancel from mathics.algorithm.introselect import introselect from mathics.algorithm.clusters import ( optimize, diff --git a/mathics/builtin/numbers/__init__.py b/mathics/builtin/numbers/__init__.py new file mode 100644 index 0000000000..2555e6b8dd --- /dev/null +++ b/mathics/builtin/numbers/__init__.py @@ -0,0 +1,3 @@ +""" +Integer and Number-Theoretical Functions +""" diff --git a/mathics/builtin/algebra.py b/mathics/builtin/numbers/algebra.py similarity index 100% rename from mathics/builtin/algebra.py rename to mathics/builtin/numbers/algebra.py diff --git a/mathics/builtin/calculus.py b/mathics/builtin/numbers/calculus.py similarity index 100% rename from mathics/builtin/calculus.py rename to mathics/builtin/numbers/calculus.py diff --git a/mathics/builtin/constants.py b/mathics/builtin/numbers/constants.py similarity index 100% rename from mathics/builtin/constants.py rename to mathics/builtin/numbers/constants.py diff --git a/mathics/builtin/diffeqns.py b/mathics/builtin/numbers/diffeqns.py similarity index 100% rename from mathics/builtin/diffeqns.py rename to mathics/builtin/numbers/diffeqns.py diff --git a/mathics/builtin/exptrig.py b/mathics/builtin/numbers/exptrig.py similarity index 100% rename from mathics/builtin/exptrig.py rename to mathics/builtin/numbers/exptrig.py diff --git a/mathics/builtin/integer.py b/mathics/builtin/numbers/integer.py similarity index 100% rename from mathics/builtin/integer.py rename to mathics/builtin/numbers/integer.py diff --git a/mathics/builtin/linalg.py b/mathics/builtin/numbers/linalg.py similarity index 100% rename from mathics/builtin/linalg.py rename to mathics/builtin/numbers/linalg.py diff --git a/mathics/builtin/numbertheory.py b/mathics/builtin/numbers/numbertheory.py similarity index 100% rename from mathics/builtin/numbertheory.py rename to mathics/builtin/numbers/numbertheory.py diff --git a/mathics/builtin/randomnumbers.py b/mathics/builtin/numbers/randomnumbers.py similarity index 100% rename from mathics/builtin/randomnumbers.py rename to mathics/builtin/numbers/randomnumbers.py diff --git a/mathics/builtin/options.py b/mathics/builtin/options.py index a857db24e3..9cddae70c7 100644 --- a/mathics/builtin/options.py +++ b/mathics/builtin/options.py @@ -5,6 +5,7 @@ """ from mathics.version import __version__ # noqa used in loading to check consistency. + from mathics.builtin.base import Builtin, Test, get_option from mathics.core.expression import ( Symbol, @@ -14,8 +15,7 @@ ensure_context, strip_context, ) -from mathics.builtin.image import Image -from mathics.core.expression import strip_context +from mathics.builtin.drawing.image import Image class Options(Builtin): diff --git a/mathics/builtin/specialfns/expintegral.py b/mathics/builtin/specialfns/expintegral.py index 89cc79d076..53401c80a8 100644 --- a/mathics/builtin/specialfns/expintegral.py +++ b/mathics/builtin/specialfns/expintegral.py @@ -7,6 +7,7 @@ from mathics.version import __version__ # noqa used in loading to check consistency. from mathics.builtin.arithmetic import _MPMathFunction +from mathics.core.expression import from_mpmath class ExpIntegralE(_MPMathFunction): """ diff --git a/setup.py b/setup.py index da2af5109d..ce1c389de7 100644 --- a/setup.py +++ b/setup.py @@ -64,17 +64,27 @@ def read(*rnames): EXTENSIONS = [] CMDCLASS = {} else: - EXTENSIONS = { - "core": ["expression", "numbers", "rules", "pattern"], + EXTENSIONS_DICT = { + "core": ("expression", "numbers", "rules", "pattern"), "builtin": ["arithmetic", "numeric", "patterns", "graphics"], } EXTENSIONS = [ Extension( "mathics.%s.%s" % (parent, module), ["mathics/%s/%s.py" % (parent, module)] ) - for parent, modules in EXTENSIONS.items() + for parent, modules in EXTENSIONS_DICT.items() for module in modules ] + # EXTENSIONS_SUBDIR_DICT = { + # "builtin": [("numbers", "arithmetic"), ("numbers", "numeric"), ("drawing", "graphics")], + # } + # EXTENSIONS.append( + # Extension( + # "mathics.%s.%s.%s" % (parent, module[0], module[1]), ["mathics/%s/%s/%s.py" % (parent, module[0], module[1])] + # ) + # for parent, modules in EXTENSIONS_SUBDIR_DICT.items() + # for module in modules + # ) CMDCLASS = {"build_ext": build_ext} INSTALL_REQUIRES += ["cython>=0.15.1"] @@ -113,6 +123,8 @@ def subdirs(root, file="*.*", depth=10): "mathics.core.parser", "mathics.builtin", "mathics.builtin.compile", + "mathics.builtin.drawing", + "mathics.builtin.numbers", "mathics.builtin.numpy_utils", "mathics.builtin.pymimesniffer", "mathics.builtin.pympler", diff --git a/test/test_assumptions.py b/test/test_assumptions.py new file mode 100644 index 0000000000..78cb2e20fc --- /dev/null +++ b/test/test_assumptions.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +from .helper import check_evaluation +import pytest + +from mathics_scanner.errors import IncompleteSyntaxError + +list_test_assumptions_integrate = [ + ( + "Integrate[x^n, {x, 0, 1}]", + "Piecewise[{{1 / (1 + n), 1 + Re[n] > 0 && n > -Infinity && n < Infinity && n != -1}}, Infinity]", + "This is so complicated due the sympy result is wrong...", + ), + ( + "Assuming[0 < n < 1, Integrate[x^n, {x, 0, 1}]]", + "Piecewise[{{1 / (1 + n), 1 + Re[n] > 0 && n > -Infinity && n < Infinity && n != -1}}, Infinity]", + "", + ), + ( + "Assuming[0 < Re[n] + 1, Integrate[x^n, {x, 0, 1}]]", + "Piecewise[{{1 / (1 + n), n > -Infinity && n < Infinity && n != -1}}, Infinity]", + "", + ), + ("Assuming[n == 1, Integrate[x^n, {x, 0, 1}]]", "1 / 2", ""), + ("Assuming[n == 2, Integrate[x^n, {x, 0, 1}]]", "1 / 3", ""), + ("Assuming[n == -1, Integrate[x^n, {x, 0, 1}]]", "Infinity", ""), + # ("Assuming[12, n>=3], Integrate[x^n, {x, 0, 1}]]", "x^(n+1)/(n+1)", ""), +] + +list_test_assumptions_simplify = [ + ("Simplify[a==b || a!=b]", "True", "",), + ("Simplify[a==b && a!=b]", "False", "",), + ("Simplify[a<=b && a>b]", "False", "",), + ("Simplify[a==b, ! a!=b]", "True", "",), + ("Simplify[a==b, a!=b]", "False", "",), + ("Simplify[a > b, {a==4}]", "b < 4", "",), + ("Simplify[And[a>b, bb", "",), + ("Simplify[Or[a>b, ab, bb", "",), + ("Simplify[a>b, {b<=a}]", "a>b", "",), +] + + +@pytest.mark.parametrize( + ("str_expr", "str_expected", "message"), list_test_assumptions_integrate, +) +@pytest.mark.xfail +def test_assumptions_integrate(str_expr, str_expected, message): + check_evaluation(str_expr, str_expected) + + +@pytest.mark.parametrize( + ("str_expr", "str_expected", "message"), list_test_assumptions_simplify, +) +@pytest.mark.xfail +def test_assumptions_simplify(str_expr, str_expected, message): + check_evaluation(str_expr, str_expected) diff --git a/test/test_color.py b/test/test_color.py index 946dff3aec..116f67af5f 100755 --- a/test/test_color.py +++ b/test/test_color.py @@ -6,7 +6,7 @@ from random import random -import mathics.builtin.colors as colors +import mathics.builtin.drawing.colors as colors from mathics.builtin.numpy_utils import array, stacked, vectorize from mathics.core.definitions import Definitions from mathics.core.evaluation import Evaluation diff --git a/test/test_files.py b/test/test_files.py index e51773600a..014a6c2220 100644 --- a/test/test_files.py +++ b/test/test_files.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- +import sys from .helper import check_evaluation, evaluate - def test_compress(): for text in ("", "abc", " "): str_expr = f'Uncompress[Compress["{text}"]]' @@ -19,12 +19,13 @@ def test_unprotected(): check_evaluation(str_expr, str_expected, message) -def test_get_and_put(): - temp_filename = evaluate('$TemporaryDirectory<>"/testfile"').to_python() - temp_filename_strip = temp_filename[1:-1] - check_evaluation(f"40! >> {temp_filename_strip}", "Null") - check_evaluation(f"<< {temp_filename_strip}", "40!") - check_evaluation(f"DeleteFile[{temp_filename}]", "Null") +if sys.platform not in ("win32",): + def test_get_and_put(): + temp_filename = evaluate('$TemporaryDirectory<>"/testfile"').to_python() + temp_filename_strip = temp_filename[1:-1] + check_evaluation(f"40! >> {temp_filename_strip}", "Null") + check_evaluation(f"<< {temp_filename_strip}", "40!") + check_evaluation(f"DeleteFile[{temp_filename}]", "Null") # I do not know what is it supposed to test with this...