I am applying a list of complicated substitution rules in cadabra. Schematically, these rules look like A_{i} -> (long expression) for some number (say 100) of the A_i. But the expression is very long.
I find that there is a surprising amount of time communicating with the Cadabra server in trying to apply these rules, versus looking for the rule myself.
Here is a sample code:
megasum := x+y.
for i in range(10000):
megasum.top().append_child(Ex( r'B'+str(i) ))
rules = []
for i in range(100):
rules.append(Ex( r'A'+str(i)+'-> @(megasum)' ))
cdbrules = ListToExList(rules)
All the rules in lst are of the form A_i (from A0 to A99) going to the same giant sum. Then I use my Python function ListToExList to convert a Python list to a cadabra list.
On my laptop, the command
substitute($A0$, cdbrules)
requires about 600,000 us to run, and it appears independent of whether I give it A0 to act on or A99.
On the other hand, if on the Python side, I manually search the list for a rule that applies, it seems to run in about 60,000 us (in the worst case) and 5,000 us (in the best case) depending on if I find the rule quickly or not. A simple implementation:
ex := A0.
for i in range(len(keys)):
if keys[i].matches(ex):
substitute(ex, rules[i])
break
where keys is a list of the LHS of the rules.
I'm trying to understand where the hangup is. My first thought is that Python has to be communicating the entire gargantuan cdbrules, and this is the bottleneck. However, I had the sense from the code that pointers are being used (or aliases to the objects) so that no actual copying is occurring. Does anyone have any ideas?
Apologies in advance for the rather ill-defined question...