Repository navigation
Find stored values such as f[1] = a through an index, not by trying every rule - #2013
tombouctou wants to merge 4 commits into
Conversation
…very rule Evaluating f[x] tried the down- (or sub-) values of f one by one, so with n stored values every lookup took time proportional to n: about 8 ms with 8000 values, and code that memoizes many values became quadratic. A rule list now gets an index of its literal rules (unconditional rules whose left-hand side is a literal expression made of symbols, integers and strings, under a head that is not Orderless, Flat or OneIdentity) by the structure of the left-hand side. Only the literal rule SameQ to the expression, if any, is tried, at its place among the other rules, which are tried in order as before. - The place comes from the sort keys by which insert_rule keeps the list ordered. A list not in that order (DownValues set as a whole), or where a literal rule shares its sort key with another rule, is not indexed. - The index belongs to the list object it was built for: a list replaced (Clear, DownValues[f] = ...) gets a new one when needed. Inserting a literal rule updates it; removing a rule or inserting another kind of rule drops it. - Lists of fewer than 8 rules are scanned as before. A lookup in a definition of 1000 stored values now takes about 10 us instead of 1.5 ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@tombouctou if we'd fix those merge conflicts, I would like to get this fix in. I looked at the conflicts, but resolving these is not something that is easily done using GitHub's web interface. |
…okup # Conflicts: # mathics/core/definitions.py # test/core/test_definitions.py
|
While this PR is good, as noted elsewhere and in other issues or PRs, the code has a problem with how it handles the order of definitions. So the top-level question is whether we merge this and iterate, or extend this here to handle that additional situation. The basic problem is in literal_keys.add(key)Duplicate assignments get lost. An example of this: f[1] = a
f[2] = b
f[x_] := somethingThese get the same literal index, right? But WMA needs to know to try rule matching in the order given by the assignments. |
…ral rules Rename literal_keys to literal_precedences and explain that many literal rules share one sort key, which is only checked against the keys of the other rules. Test stored values assigned both before and after a pattern rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for looking closely at this. On That set doesn't hold the rules. It holds their sort keys ( For your example, I added both as tests, renamed the set to There is an assignment-order bug, but it is on master and the index doesn't touch it. This PR gives the same output as master here. If the tie-break is changed, the index stays correct: it never decides an order between two non-literal rules, and a literal rule that ties with a non-literal one turns the index off. So I'd suggest merging this and fixing the tie-break separately, either as a small follow-up PR from me or as part of #1989 if @mmatera is already changing that code. |
Ok. LGTM. Thanks for the follow-up. @mmatera: your thoughts? |
Evaluating
f[x]tries the down- (or sub-) values offone by one. With n stored values (f[1] = a,f[2] = b, …), every lookup therefore takes time proportional to n: about 8 ms with 8000 values. Code that memoizes many values becomes quadratic.This PR gives a rule list an index of its literal rules, keyed by the structure of the left-hand side. A literal rule is an unconditional rule whose left-hand side is made of symbols, integers and strings, under a head that is not
Orderless,FlatorOneIdentity. Only the literal rule that isSameQto the expression, if there is one, is tried. It is tried at its own place among the other rules, which are still tried in order as before, so rule precedence does not change.insert_rulekeeps the list ordered. A list that is not in that order (for example, afterDownValues[f] = {...}), or in which a literal rule shares its sort key with another rule, is not indexed.ClearorDownValues[f] = ...) gets a new index when one is needed. Inserting a literal rule updates the index; removing a rule, or inserting any other kind of rule, drops it.With 1000 stored values,
f[500]now takes about 10 µs instead of 1.5 ms.Checks:
test_stored_values_lookupintest/core/test_definitions.py.Context: we found this while running the lambda-algebra homology computation from the Toda-bracket project on Mathics3 (see #2001). That code stores tens of thousands of values like
BasisFunction[W][elem] = row. On one core, the change makes loading the definitions about 10% faster, and the gain grows as values accumulate.@mmatera, you mentioned on #2001 that you are reworking rule ordering and dispatch. If this overlaps with that work, I'm glad to rebase it onto your branch, or to close it in favour of yours.
🤖 Generated with Claude Code