Skip to content

Commit

Permalink
Merge branch 'master' into pretty_print
Browse files Browse the repository at this point in the history
  • Loading branch information
mmatera authored Nov 13, 2024
2 parents 4af37ff + 275aa03 commit ff2f585
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 70 deletions.
57 changes: 3 additions & 54 deletions mathics/builtin/datentime.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@
)
from mathics.core.expression import Expression
from mathics.core.list import ListExpression
from mathics.core.symbols import Symbol, SymbolNull
from mathics.core.symbols import Symbol
from mathics.core.systemsymbols import (
SymbolAborted,
SymbolAbsoluteTime,
SymbolAutomatic,
SymbolInfinity,
SymbolRowBox,
Expand Down Expand Up @@ -109,11 +110,9 @@ def total_seconds(td):
else:
total_seconds = timedelta.total_seconds

SymbolAbsoluteTime = Symbol("AbsoluteTime")
SymbolDateObject = Symbol("DateObject")
SymbolDateString = Symbol("DateString")
SymbolGregorian = Symbol("Gregorian")
SymbolPause = Symbol("Pause")


class _Date:
Expand Down Expand Up @@ -1026,39 +1025,6 @@ def eval(self, year, evaluation):
return ListExpression(year, Integer(month), Integer(day))


class Pause(Builtin):
"""
<url>:WMA link:https://reference.wolfram.com/language/ref/Pause.html</url>
<dl>
<dt>'Pause[n]'
<dd>pauses for $n$ seconds.
</dl>
>> Pause[0.5]
"""

messages = {
"numnm": (
"Non-negative machine-sized number expected at " "position 1 in `1`."
),
}

summary_text = "pause for a number of seconds"

def eval(self, n, evaluation):
"Pause[n_]"
sleeptime = n.to_python()
if not isinstance(sleeptime, (int, float)) or sleeptime < 0:
evaluation.message(
"Pause", "numnm", Expression(SymbolPause, from_python(n))
)
return

time.sleep(sleeptime)
return SymbolNull


class SystemTimeZone(Predefined):
"""
<url>
Expand Down Expand Up @@ -1103,7 +1069,7 @@ def evaluate(self, evaluation):
return Expression(SymbolDateObject.evaluate(evaluation))


if sys.platform != "win32" and not hasattr(sys, "pyston_version_info"):
if sys.platform != "emscripten":

class TimeConstrained(Builtin):
"""
Expand All @@ -1124,23 +1090,6 @@ class TimeConstrained(Builtin):
the state of the Mathics3 kernel.
"""

# FIXME: these tests sometimes cause SEGVs which probably means
# that TimeConstraint has bugs.

# Consider testing via unit tests.
# >> TimeConstrained[Integrate[Sin[x]^1000000,x],1]
# = $Aborted

# >> TimeConstrained[Integrate[Sin[x]^1000000,x], 1, Integrate[Cos[x],x]]
# = Sin[x]

# >> s=TimeConstrained[Integrate[Sin[x] ^ 3, x], a]
# : Number of seconds a is not a positive machine-sized number or Infinity.
# = TimeConstrained[Integrate[Sin[x] ^ 3, x], a]

# >> a=1; s
# = Cos[x] (-5 + Cos[2 x]) / 6

attributes = A_HOLD_ALL | A_PROTECTED
messages = {
"timc": (
Expand Down
46 changes: 45 additions & 1 deletion mathics/builtin/procedural.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
environment.
"""

import time

from mathics.core.attributes import (
A_HOLD_ALL,
Expand All @@ -24,6 +25,7 @@
A_READ_PROTECTED,
)
from mathics.core.builtin import BinaryOperator, Builtin, IterationFunction
from mathics.core.convert.python import from_python
from mathics.core.evaluation import Evaluation
from mathics.core.expression import Expression
from mathics.core.interrupt import (
Expand All @@ -34,7 +36,7 @@
WLThrowInterrupt,
)
from mathics.core.symbols import Symbol, SymbolFalse, SymbolNull, SymbolTrue
from mathics.core.systemsymbols import SymbolMatchQ
from mathics.core.systemsymbols import SymbolMatchQ, SymbolPause
from mathics.eval.patterns import match

SymbolWhich = Symbol("Which")
Expand Down Expand Up @@ -438,6 +440,48 @@ def eval(self, evaluation: Evaluation):
raise AbortInterrupt


class Pause(Builtin):
"""
<url>:WMA link:https://reference.wolfram.com/language/ref/Pause.html</url>
<dl>
<dt>'Pause[n]'
<dd>pauses for at least $n$ seconds.
</dl>
>> Pause[0.5]
"""

messages = {
"numnm": (
"Non-negative machine-sized number expected at " "position 1 in `1`."
),
}

summary_text = "pause for a number of seconds"

# Number of timeout polls per second that we perform in looking
# for a timeout.
PAUSE_TICKS_PER_SECOND = 1000

def eval(self, n, evaluation: Evaluation):
"Pause[n_]"
sleeptime = n.to_python()
if not isinstance(sleeptime, (int, float)) or sleeptime < 0:
evaluation.message(
"Pause", "numnm", Expression(SymbolPause, from_python(n))
)
return

steps = int(self.PAUSE_TICKS_PER_SECOND * sleeptime)
for _ in range(steps):
time.sleep(1 / self.PAUSE_TICKS_PER_SECOND)
if evaluation.timeout:
return SymbolNull

return SymbolNull


class Return(Builtin):
"""
<url>:WMA link:
Expand Down
12 changes: 3 additions & 9 deletions mathics/core/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
from mathics.core.convert.expression import to_expression
from mathics.core.convert.op import ascii_operator_to_symbol
from mathics.core.convert.python import from_bool
from mathics.core.convert.sympy import from_sympy, to_numeric_sympy_args
from mathics.core.convert.sympy import from_sympy
from mathics.core.definitions import Definition, Definitions
from mathics.core.evaluation import Evaluation
from mathics.core.exceptions import MessageException
Expand Down Expand Up @@ -91,6 +91,7 @@
from mathics.eval.numbers.numbers import cancel
from mathics.eval.numerify import numerify
from mathics.eval.scoping import dynamic_scoping
from mathics.eval.sympy import eval_sympy

try:
import ujson
Expand Down Expand Up @@ -582,14 +583,7 @@ def eval(self, z, evaluation: Evaluation):
# converted to python and the result is converted from sympy
#
# "%(name)s[z__]"
sympy_args = to_numeric_sympy_args(z, evaluation)
if self.sympy_name is None:
return
sympy_fn = getattr(sympy, self.sympy_name)
try:
return from_sympy(tracing.run_sympy(sympy_fn, *sympy_args))
except Exception:
return
return eval_sympy(self, z, evaluation)

def get_constant(self, precision, evaluation, have_mpmath=False):
try:
Expand Down
3 changes: 2 additions & 1 deletion mathics/core/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def run_with_timeout_and_stack(request, timeout, evaluation):
if thread.is_alive():
evaluation.timeout = True
while thread.is_alive():
time.sleep(0.001)
pass
evaluation.timeout = False
evaluation.stopped = False
Expand All @@ -140,7 +141,7 @@ def run_with_timeout_and_stack(request, timeout, evaluation):
if success:
return result
else:
raise result[0].with_traceback(result[1], result[2])
raise result[1].with_traceback(result[2])


class _Out(KeyComparable):
Expand Down
1 change: 1 addition & 0 deletions mathics/core/systemsymbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@
SymbolPath = Symbol("System`$Path")
SymbolPattern = Symbol("System`Pattern")
SymbolPatternTest = Symbol("System`PatternTest")
SymbolPause = Symbol("System`Pause")
SymbolPi = Symbol("System`Pi")
SymbolPiecewise = Symbol("System`Piecewise")
SymbolPlot = Symbol("System`Plot")
Expand Down
3 changes: 3 additions & 0 deletions mathics/docpipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,9 @@ def main():
test_pipeline.print_and_log(
f"Test evaluation took {datetime.now() - start_time} seconds"
)
test_pipeline.print_and_log(
f"Test finished at {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}"
)

if args.show_statistics:
show_lru_cache_statistics()
Expand Down
84 changes: 84 additions & 0 deletions mathics/eval/sympy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""
Evaluation of SymPy functions
"""

import sys
from queue import Queue
from threading import Thread
from typing import Optional

import sympy

import mathics.eval.tracing as tracing
from mathics.core.convert.sympy import from_sympy, to_numeric_sympy_args
from mathics.core.element import BaseElement
from mathics.core.evaluation import Evaluation


def eval_sympy_unconstrained(
self, z: BaseElement, evaluation: Evaluation
) -> Optional[BaseElement]:
"""
Evaluate element `z` converting it to SymPy and back to Mathics3.
If an exception is raised we return None.
This version is called not-wrapped in a thread on systems like
emscripten that do not support Python-style threading.
"""
sympy_args = to_numeric_sympy_args(z, evaluation)
if self.sympy_name is None:
return
sympy_fn = getattr(sympy, self.sympy_name)
try:
return from_sympy(tracing.run_sympy(sympy_fn, *sympy_args))
except Exception:
return


def eval_sympy_with_timeout(
self, z: BaseElement, evaluation: Evaluation
) -> Optional[BaseElement]:
"""
Evaluate an element `z` converting it to SymPy,
and back to Mathics3.
If an exception is raised we return None.
This version is run in a thread, and checked for evaluation timeout.
"""

if evaluation.timeout is None:
return eval_sympy_unconstrained(self, z, evaluation)

def _thread_target(queue) -> None:
try:
result = eval_sympy_unconstrained(self, z, evaluation)
queue.put((True, result))
except BaseException:
exc_info = sys.exc_info()
queue.put((False, exc_info))

queue = Queue(maxsize=1) # stores the result or exception

thread = Thread(target=_thread_target, args=(queue,))
thread.start()
while thread.is_alive():
thread.join(0.001)
if evaluation.timeout:
# I can kill the thread.
# just leave it...
return None

# pick the result and return
success, result = queue.get()
if success:
return result
else:
raise result[1].with_traceback(result[2])


# Common top-level evaluation SymPy "eval" function:
eval_sympy = (
eval_sympy_unconstrained
if sys.platform in ("emscripten",)
else eval_sympy_with_timeout
)
Loading

0 comments on commit ff2f585

Please sign in to comment.