Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/ubuntu-minimal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Mathics (ubuntu-minimal)

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
env:
NO_CYTHON: 1
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: [3.9]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
sudo apt-get update -qq && sudo apt-get install -qq liblapack-dev llvm-dev
python -m pip install --upgrade pip
# Can remove after next Mathics-Scanner release
# python -m pip install -e git+https://github.com/Mathics3/mathics-scanner#egg=Mathics-Scanner[full]
- name: Install Mathics with full dependencies
run: |
make develop
- name: Test Mathics
run: |
make -j3 check
2 changes: 1 addition & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ jobs:
python setup.py install
- name: Test Mathics
run: |
pip install -e .[dev,full]
pip install -e .[dev]
set PYTEST_WORKERS="-n3"
make check
20 changes: 14 additions & 6 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ Enhancements
* In assignment to messages associated with symbols, the attribute ``Protected`` is not having into account, following the standard in WMA. With this and the above change, Combinatorical 2.0 works as written.
* ``Share[]`` performs an explicit call to the Python garbage collection and returns the amount of memory free.
* Improving the compatibility of ``TeXForm`` and ``MathMLForm`` outputs with WMA. MatML tags around numbers appear as "<mn>" tags instead of "<mtext>", except in the case of ``InputForm`` expressions. In TeXForm some quotes around strings have been removed to conform to WMA. It is not clear whether this is the correct behavior.


* Allow scipy and skimage to be optional. In particular:
- Revise ``Nintegrate[]`` use ``Method="Internal"`` when scipy isn't available
* Pyston up to versions from 2.2 to 2.3.4 are supported as are PyPy versions from 3.7-7.3.9.0 up 3.9-7.3.9. However those Python interpreters may have limitations and limitations on packages that they support.

Documentation
.............
Expand Down Expand Up @@ -46,19 +47,16 @@ Internals
* ``Definition`` has a new property ``is_numeric``.
* ``Symbol.is_numeric`` and ``Expression.is_numeric`` now uses the attribute ``Definition.is_numeric`` to determine the returned value.
* ``NIntegrate`` internal algorithms and interfaces to ``scipy`` were moved to ``mathics.algorithm.integrators`` and ``mathics.builtin.scipy_utils.integrators`` respectively.
* To speed up attributes read, and RAM usage, attributes are now stored in a bitset instead of a tuple of strings.
* Definitions for symbols ``CurrentContext`` and ``ContextPath[]`` are mirrored in the ``mathics.core.definitions.Definitions`` object for faster access.
* To speed up the lookup of symbols names, ``Definitions`` object now have two properties: ``current_context`` and ``context_path``. These properties stores the values of the corresponding symbols in the `builtin` definitions.

* ``FullForm[List[...]]`` now is shown as ``{...}`` according to the WL standard.
* ``Expression.is_numeric()`` accepts an ``Evaluation`` object as a parameter; the definitions attribute of that is used.
* ``apply_N`` was introduced in module ``mathics.builtin.numeric`` to speed up the critical built-in function``N``. Its use instead of the idiom ``Expression(SymbolN, expr, prec).evaluate(evaluation)`` makes the evaluation faster.
* A failure in the order in which ``mathics.core.definitions`` stores the rules was fixed.
* ``any`` /``all`` calls were unrolled as loops in Cythonized modules: this avoids the overhead of a function call replacing it by a (C) for loop, which is faster.
* ``BaseExpression.get_head`` now avoids building a symbol and then look for its name. It saves two function calls.
* ``SameQ`` first checks type, then ``id``s, and then names in symbols.
* In ``mathics.builtin.patterns.PatternTest``, if the condition is one of the most used tests (``NumberQ``, ``NumericQ``, ``StringQ``, etc) the ``match`` method is overwritten to specialized versions that avoid function calls.
* in the same aim, ``mathics.core.patterns.AtomPattern`` now specializes the comparison depending of the ``Atom`` type.
* To speed up the Mathics ``Expression`` manipulation code, `Symbol`s objects are now a singleton class. This avoids a lot of unnecesary string comparisons, and calls to ``ensure_context``.
* To speed up development, you can set ``NO_CYTHON`` to skip Cythonizing Python modules
* A bug was fixed relating to the order in which ``mathics.core.definitions`` stores the rules
* Improved support for ``Series`` Issue #46.
Expand All @@ -69,6 +67,15 @@ Internals
* ``N[_,_,Method->method]`` was reworked. Issue #137.
* The methods ``boxes_to_*`` were moved to ``BoxExpression``.
* remove ``flatten_*`` from the ``Atom`` interface.
*

Speed improvements:
..................

* In ``Expression`` manipulation code, `Symbol`s objects are now a singleton class. This avoids a lot of unnecesary string comparisons, and calls to ``ensure_context``.
* Attributes are now stored in a bitset instead of a tuple of strings.
* To speed up the lookup of symbols names, ``Definitions`` object now have two properties: ``current_context`` and ``context_path``. These properties stores the values of the corresponding symbols in the ``builtin`` definitions.
* ``apply_N`` was add to speed up the then often-used built-in function ``N``.


Package update
Expand Down Expand Up @@ -97,6 +104,7 @@ Bugs
* Partial fix of ``FillSimplify``
* Streams used in MathicsOpen are now freed and their file descriptors now released. Issue #326.
* Some temporary files that were created are now removed from the filesystem. Issue #309.
* There were a number of small changes/fixes involving ``NIntegrate`` and its Method options. ``Nintegrate`` tests have been expanded.

4.0.1
-----
Expand Down
5 changes: 4 additions & 1 deletion mathics/algorithm/optimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,16 +360,19 @@ def sub(evaluation):
return x0, True


native_optimizer_messages = {}

native_local_optimizer_methods = {
"Automatic": find_minimum_newton1d,
"newton": find_minimum_newton1d,
"Newton": find_minimum_newton1d,
}

native_findroot_methods = {
"Automatic": find_root_newton,
"Newton": find_root_newton,
"Secant": find_root_secant,
}
native_findroot_messages = {}


def is_zero(
Expand Down
1 change: 1 addition & 0 deletions mathics/builtin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
SympyObject,
Operator,
PatternObject,
check_requires_list,
)


Expand Down
20 changes: 11 additions & 9 deletions mathics/builtin/arithfns/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class Plus(BinaryOperator, SympyFunction):
def format_plus(self, items, evaluation):
"Plus[items__]"

def negate(item):
def negate(item): # -> Expression (see FIXME below)
if item.has_form("Times", 1, None):
if isinstance(item.elements[0], Number):
neg = -item.elements[0]
Expand All @@ -333,11 +333,13 @@ def negate(item):
else:
return Expression(SymbolTimes, IntegerM1, *item.elements)
elif isinstance(item, Number):
# FIXME: Isn't this is the wrong type? We should be
# returning an Expression,
return -item.to_sympy()
else:
return Expression(SymbolTimes, IntegerM1, item)

def is_negative(value):
def is_negative(value) -> bool:
if isinstance(value, Complex):
real, imag = value.to_sympy().as_real_imag()
if real <= 0 and imag <= 0:
Expand All @@ -346,18 +348,18 @@ def is_negative(value):
return True
return False

items = items.get_sequence()
values = [to_expression(SymbolHoldForm, item) for item in items[:1]]
elements = items.get_sequence()
values = [to_expression(SymbolHoldForm, element) for element in elements[:1]]
ops = []
for item in items[1:]:
for element in elements[1:]:
if (
item.has_form("Times", 1, None) and is_negative(item.elements[0])
) or is_negative(item):
item = negate(item)
element.has_form("Times", 1, None) and is_negative(element.elements[0])
) or is_negative(element):
element = negate(element)
op = "-"
else:
op = "+"
values.append(Expression(SymbolHoldForm, item))
values.append(Expression(SymbolHoldForm, element))
ops.append(String(op))
return Expression(
SymbolInfix,
Expand Down
62 changes: 40 additions & 22 deletions mathics/builtin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import importlib
from itertools import chain
import typing
from typing import Any, Iterable, cast
from typing import Any, Callable, Iterable, List, Optional, cast


from mathics.builtin.exceptions import (
Expand All @@ -31,6 +31,7 @@
MachineReal,
PrecisionReal,
String,
from_python,
)
from mathics.core.expression import Expression, SymbolDefault, to_expression
from mathics.core.number import get_precision, PrecisionValueError
Expand All @@ -42,6 +43,21 @@
from mathics.core.attributes import protected, read_protected


def check_requires_list(requires: list) -> bool:
"""
Check if module names in ``requires`` can be imported and return True if they can or False if not.
"""
for package in requires:
lib_is_installed = True
try:
lib_is_installed = importlib.util.find_spec(package) is not None
except ImportError:
lib_is_installed = False
if not lib_is_installed:
return False
return True


def get_option(options, name, evaluation, pop=False, evaluate=True):
# we do not care whether an option X is given as System`X,
# Global`X, or with any prefix from $ContextPath for that
Expand Down Expand Up @@ -112,7 +128,7 @@ class Builtin:
```
def apply(x, evaluation):
"F[x_Real]"
return Expression(Symbol("G"), x*2)
return Expression("G", x*2)
```

adds a ``BuiltinRule`` to the symbol's definition object that implements ``F[x_]->G[x*2]``.
Expand Down Expand Up @@ -407,7 +423,6 @@ def get_functions(self, prefix="apply", is_pymodule=False):
unavailable_function = self._get_unavailable_function()
for name in dir(self):
if name.startswith(prefix):

function = getattr(self, name)
pattern = function.__doc__
if pattern is None: # Fixes PyPy bug
Expand Down Expand Up @@ -439,25 +454,28 @@ def get_functions(self, prefix="apply", is_pymodule=False):
def get_option(options, name, evaluation, pop=False):
return get_option(options, name, evaluation, pop)

def _get_unavailable_function(self):
requires = getattr(self, "requires", [])

for package in requires:
try:
importlib.import_module(package)
except ImportError:

def apply(**kwargs): # will override apply method
kwargs["evaluation"].message(
"General",
"pyimport", # see inout.py
strip_context(self.get_name()),
package,
)
def _get_unavailable_function(self) -> Optional[Callable]:
"""
If some of the required libraries for a symbol are not available,
returns a default function that override the ``apply_`` methods
of the class. Otherwise, returns ``None``.
"""

return apply
def apply_unavailable(**kwargs): # will override apply method
package = from_python(self.requires)
kwargs["evaluation"].message(
"General",
"pyimport", # see inout.py
strip_context(self.get_name()),
# WARNING: package isn't defined here, but mysteriously is
# defined in the place gets apply (or rather "eval"'d).
# Without it we there are a number of doctests we won't run
# even though they are should be.
package, # noqa
)

return None
requires = getattr(self, "requires", [])
return None if check_requires_list(requires) else apply_unavailable

def get_option_string(self, *params):
s = self.get_option(*params)
Expand Down Expand Up @@ -543,7 +561,7 @@ def get_operator_display(self) -> typing.Optional[str]:


class Predefined(Builtin):
def get_functions(self, prefix="apply", is_pymodule=False):
def get_functions(self, prefix="apply", is_pymodule=False) -> List[Callable]:
functions = list(super().get_functions(prefix))
if prefix == "apply" and hasattr(self, "evaluate"):
functions.append((Symbol(self.get_name()), self.evaluate))
Expand Down Expand Up @@ -721,7 +739,7 @@ class BoxExpression(BuiltinElement):
# considered "inert". However, it could happend that an Expression having them as an element
# be evaluable, and try to apply rules. For example,
# InputForm[ToBoxes[a+b]]
# should be evaluated to ``Expression(SymbolRowBox, '"a"', '"+"', '"b"')``.
# should be evaluated to ``Expression("RowBox", '"a"', '"+"', '"b"')``.
#
# Changes to do, after the refactor of mathics.core:
#
Expand Down
2 changes: 1 addition & 1 deletion mathics/builtin/datentime.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ def apply_makeboxes(self, datetime, gran, cal, tz, fmt, evaluation):
fmtds = Expression(SymbolDateString, datetime, fmt).evaluate(evaluation)
if fmtds is None:
return
# tz = Expression("ToString", tz).evaluate(evaluation)
# tz_string = to_mathic_expression("ToString", tz).evaluate(evaluation)
tz_string = String(str(int(tz.to_python())))
return to_expression(
SymbolRowBox, to_mathics_list("[", fmtds, " GTM", tz_string, "]")
Expand Down
5 changes: 3 additions & 2 deletions mathics/builtin/distance/stringdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@

from mathics.builtin.base import Builtin

from mathics.core.atoms import Integer, String, Symbol
from mathics.core.expression import Expression
from mathics.core.atoms import Integer, String
from mathics.core.symbols import SymbolTrue

SymbolEditDistance = Symbol("EditDistance")

# Levenshtein's algorithm is defined by the following construction:
# (adapted from https://de.wikipedia.org/wiki/Levenshtein-Distanz)
Expand Down Expand Up @@ -139,7 +140,7 @@ def normalize(c):
elif a.get_head_name() == "System`List" and b.get_head_name() == "System`List":
return Integer(self._distance(a.leaves, b.leaves, lambda u, v: u.sameQ(v)))
else:
return Expression("EditDistance", a, b)
return Expression(SymbolEditDistance, a, b)


class DamerauLevenshteinDistance(_StringDistance):
Expand Down
Loading