diff --git a/CHANGES.rst b/CHANGES.rst
index 34320f9a7e..5a1134a04b 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -11,6 +11,8 @@ Enhancements
* ``ToString`` accepts an optional *form* parameter.
* ``ToExpression`` handles multi-line string input
* ``FileNames`` returns a sorted list (#1250).
+* ``ReplaceRepeated`` and ``FixedPoint`` now supports the ``MaxIteration`` option (#1260).
+* ``FixedPoint`` now supports the ``SameTest`` option.
Bug fixes
+++++++++
diff --git a/mathics/builtin/control.py b/mathics/builtin/control.py
index 483e3a04b6..9e4c007fe5 100644
--- a/mathics/builtin/control.py
+++ b/mathics/builtin/control.py
@@ -506,7 +506,7 @@ class FixedPoint(Builtin):
'FixedPoint[$f$, $expr$]'
starting with $expr$, iteratively applies $f$ until the result no longer changes.
'FixedPoint[$f$, $expr$, $n$]'
- performs at most $n$ iterations.
+ performs at most $n$ iterations. The same that using $MaxIterations->n$
>> FixedPoint[Cos, 1.0]
@@ -524,9 +524,13 @@ class FixedPoint(Builtin):
= 0.739085
"""
- def apply(self, f, expr, n, evaluation):
- "FixedPoint[f_, expr_, n_:DirectedInfinity[1]]"
+ options = {
+ "MaxIterations": "Infinity",
+ "SameTest": "Automatic",
+ }
+ def apply(self, f, expr, n, evaluation, options):
+ "FixedPoint[f_, expr_, n_:DirectedInfinity[1], OptionsPattern[FixedPoint]]"
if n == Expression("DirectedInfinity", 1):
count = None
else:
@@ -534,14 +538,32 @@ def apply(self, f, expr, n, evaluation):
if count is None or count < 0:
evaluation.message("FixedPoint", "intnn")
return
+
+ if count is None:
+ count = self.get_option(options, "MaxIterations", evaluation)
+ if count.is_numeric():
+ count = count.get_int_value()
+ else:
+ count = None
+
result = expr
index = 0
+ sametest = self.get_option(options, "SameTest", evaluation)
+ if sametest == Symbol("Automatic"):
+ sametest = None
+
while count is None or index < count:
evaluation.check_stopped()
new_result = Expression(f, result).evaluate(evaluation)
- if new_result == result:
- result = new_result
- break
+ if sametest:
+ same = Expression(sametest, result, new_result).evaluate(evaluation)
+ same = same.is_true()
+ if same:
+ break
+ else:
+ if new_result == result:
+ result = new_result
+ break
result = new_result
index += 1
diff --git a/mathics/builtin/patterns.py b/mathics/builtin/patterns.py
index 033753318c..0d9f3e4c1e 100644
--- a/mathics/builtin/patterns.py
+++ b/mathics/builtin/patterns.py
@@ -305,6 +305,11 @@ class ReplaceRepeated(BinaryOperator):
>> a+b+c //. c->d
= a + b + d
+ >> f = ReplaceRepeated[c->d];
+ >> f[a+b+c]
+ = a + b + d
+ >> Clear[f];
+
Simplification of logarithms:
>> logrules = {Log[x_ * y_] :> Log[x] + Log[y], Log[x_ ^ y_] :> y * Log[x]};
>> Log[a * (b * c) ^ d ^ e * f] //. logrules
@@ -324,8 +329,16 @@ class ReplaceRepeated(BinaryOperator):
"rmix": "Elements of `1` are a mixture of lists and nonlists.",
}
- def apply_list(self, expr, rules, evaluation):
- "ReplaceRepeated[expr_, rules_]"
+ options = {
+ "MaxIterations": "65535",
+ }
+
+ rules = {
+ "ReplaceRepeated[rules_][expr_]": "ReplaceRepeated[expr, rules]",
+ }
+
+ def apply_list(self, expr, rules, evaluation, options):
+ "ReplaceRepeated[expr_, rules_, OptionsPattern[ReplaceRepeated]]"
try:
rules, ret = create_rules(rules, expr, "ReplaceRepeated", evaluation)
except PatternError:
@@ -335,8 +348,17 @@ def apply_list(self, expr, rules, evaluation):
if ret:
return rules
+ maxit = self.get_option(options, "MaxIterations", evaluation)
+ if maxit.is_numeric():
+ maxit = maxit.get_int_value()
+ else:
+ maxit = -1
+
while True:
evaluation.check_stopped()
+ if maxit == 0:
+ break
+ maxit -= 1
result, applied = expr.apply_rules(rules, evaluation)
if applied:
result = result.evaluate(evaluation)