Skip to content

Commit e9114d4

Browse files
mmaterarocky
andauthored
Using upvalues of atoms in evaluations. (#1507)
In the `Expression.rewrite_apply_eval_step` method, in the upvalues lookup, to reduce the cost of evaluating expressions with many numerical elements, we use to skip atomic elements. This makes that upvalues of atomic elements get never evaluated (see #1506). This PR fix by, instead of skipping atoms, just check beforehand if all the elements are of the same kind. --------- Co-authored-by: R. Bernstein <[email protected]>
1 parent 5cbd6b8 commit e9114d4

File tree

7 files changed

+56
-13
lines changed

7 files changed

+56
-13
lines changed

mathics/builtin/layout.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class Format(Builtin):
6060
6161
Raw objects cannot be formatted:
6262
>> Format[3] = "three";
63-
: Cannot assign to raw object 3.
63+
: Tag Integer in 3 is Protected.
6464
6565
Format types must be symbols:
6666
>> Format[r, a + b] = "r";

mathics/core/assignment.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ def get_symbol_values(
102102
A list of rules. None if `symbol` is not a Symbol.
103103
104104
"""
105-
name = symbol.get_name()
106-
if not name:
105+
if not isinstance(symbol, Symbol):
107106
evaluation.message(func_name, "sym", symbol, 1)
108107
return None
108+
name = symbol.get_name()
109109
definitions = evaluation.definitions
110110
try:
111111
definition = (

mathics/core/element.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ class ElementsProperties:
7777
# this True elements are not sorted can cause evaluation differences.
7878
is_ordered: bool = False
7979

80+
# Uniform expressions have all their elements with the same Head.
81+
is_uniform: bool = False
82+
8083

8184
class ImmutableValueMixin:
8285
@property

mathics/core/expression.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,25 @@ def _build_elements_properties(self):
355355
"""
356356

357357
# All of the properties start out optimistic (True) and are reset when that proves wrong.
358-
self.elements_properties = ElementsProperties(True, True, True)
358+
self.elements_properties = ElementsProperties(True, True, True, True)
359359

360360
last_element = None
361361
values = []
362+
last_lookup_name = ""
363+
uniform = True
362364
for element in self._elements:
363365
# Test for the literalness, and the three properties mentioned above
364366
if not element.is_literal:
365367
self.elements_properties.elements_fully_evaluated = False
366368

369+
if uniform:
370+
lookup_name = element.get_lookup_name()
371+
if last_lookup_name:
372+
if lookup_name != last_lookup_name:
373+
uniform = self.elements_properties.is_uniform = False
374+
else:
375+
last_lookup_name = lookup_name
376+
367377
if isinstance(element, Expression):
368378
# "self" can't be flat.
369379
self.elements_properties.is_flat = False
@@ -1296,6 +1306,7 @@ def flatten_callback(new_elements, old):
12961306
else:
12971307
return threaded, True
12981308

1309+
elements_properties = new.elements_properties
12991310
# Step 6:
13001311
# Look at the rules associated with:
13011312
# 1. the upvalues of each element
@@ -1335,15 +1346,17 @@ def flatten_callback(new_elements, old):
13351346
def rules():
13361347
rules_names = set()
13371348
if not A_HOLD_ALL_COMPLETE & attributes:
1338-
for element in elements:
1339-
if not isinstance(element, EvalMixin):
1340-
continue
1349+
sample_elements = (
1350+
(elements[0],)
1351+
if elements and elements_properties.is_uniform
1352+
else elements
1353+
)
1354+
for element in sample_elements:
13411355
name = element.get_lookup_name()
1342-
if len(name) > 0: # only lookup rules if this is a symbol
1343-
if name not in rules_names:
1344-
rules_names.add(name)
1345-
for rule in evaluation.definitions.get_upvalues(name):
1346-
yield rule
1356+
if name and name not in rules_names:
1357+
rules_names.add(name)
1358+
for rule in evaluation.definitions.get_upvalues(name):
1359+
yield rule
13471360
lookup_name = new.get_lookup_name()
13481361
if lookup_name == new.get_head_name():
13491362
for rule in evaluation.definitions.get_downvalues(lookup_name):

mathics/core/symbols.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,13 @@ def get_head_name(self) -> "str":
216216
# 1/0
217217
# return None if stop_on_error else {}
218218

219+
def get_lookup_name(self) -> str:
220+
"""
221+
By default, atoms that are not symbols
222+
have their class head_names as their lookup names.
223+
"""
224+
return self.class_head_name
225+
219226
@property
220227
def element_order(self) -> tuple:
221228
"""
@@ -461,6 +468,12 @@ def get_head(self) -> "Symbol":
461468
def get_head_name(self) -> str:
462469
return "System`Symbol"
463470

471+
def get_lookup_name(self) -> str:
472+
"""
473+
The lookup name of a Symbol is its name.
474+
"""
475+
return self.get_name()
476+
464477
def get_option_values(self, evaluation, allow_symbols=False, stop_on_error=True):
465478
"""
466479
Build a dictionary of options from an expression.

mathics/eval/assignments/assignment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ def eval_assign_format(
508508
lhs_reference = get_reference_expression(lhs)
509509
lhs_reference = (
510510
lhs_reference.get_head()
511-
if isinstance(lhs_reference, Expression)
511+
if not isinstance(lhs_reference, Symbol)
512512
else lhs_reference
513513
)
514514
tags = process_tags_and_upset_dont_allow_custom(

test/test_evaluation.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,20 @@ def test_system_specific_long_integer():
289289
# check_evaluation_with_err(str_expr, str_expected, message)
290290

291291

292+
def test_eval_atom_upvalues():
293+
"""Check that upvalues of atoms are taken into account in evaluations"""
294+
# Clear definitions
295+
check_evaluation(None, None, None)
296+
check_evaluation(
297+
"Unprotect[Real]; Real/:F[x_Real]:=x; DownValues[F]",
298+
"{}",
299+
"F does not have downvalues",
300+
)
301+
check_evaluation("F[3.]", "3.", "Upvalue of Real is taken into account.")
302+
# Clear definitions again.
303+
check_evaluation(None, None, None)
304+
305+
292306
def test_exit():
293307
global session
294308
try:

0 commit comments

Comments
 (0)