I'm currently exermining sympy's subs system and I want to share my
observations of the currently implemented algorithm and propose some -
hopefully favorable - changes.
Let's first look what currently happens if subs is called:
------ _subs_dict -----
| |
subs --------------------------- _subs_list ---- subs (for every item
in list)
|
---- _subs_old_new ---- _eval_subs
So subs is mainly just used to dispatch different input types to their
appropriate handler functions. Ultimatively they all come to the case
_subs_old_new by splitting up a sequence of arguments into a
consecutevily call of subs. Here is the first thing I
don't like in the moment: The current behavour also allows sequences
of sequences of (...) as arguments and applys them recursively. Apart
from as I think not being very useful it might also confuse _subs_dict
which tries its best to find a right order for substituting. That
might only be a minor problem and I haven't tried to find examples
where this actually fails but I would nevertheless prefer that
_subs_list calls directly _subs_old_new after making sure that the
elements are propper sympy objects.
------ _subs_dict -----
| |
subs --------------------------- _subs_list ---- _subs_old_new (for
every item in the sequence)
|
---- _subs_old_new ---- _eval_subs
Maybe it would also be better to squash _subs_dict and _subs_list into
subs since they all are not too long or complex and as far as I could
find out nowhere overwritten. This would maybe be a bit faster and
also make more place on the stack.
subs ---- _subs_old_new (for every item in the sequence) ----
_eval_subs
Maybe even _subs_old_new should be squashed.
subs ---- _eval_subs (for every item in the sequence)
However, the important part in the moment is the _eval_subs method
which is implemented by various subclasses. Unfortunatly this results
in a reimplementation of the matching algorithm. For example the add
class has a pretty complex matching logic in its _eval_subs and some
equally complex logic in AssocOp._matches_commutative which is used
for matching.
I think it would be much cleaner to have the following instead:
* substitution should be dependend of the pattern it should
substitute. If the pattern is atomic it's easy to do and some
recursive method "_subs_atomic" can take care of this. It should be
possible to only define it in Basic and let all other classes inherit
it. Absolutely no matching algorithm is required for this.
* substitution of more complicated patterns should be achieved by
using the matching functionality. Basically match(expr, pattern) is
used recursively on each subexpression. If the pattern matches this
will return substitution rules for atomic patterns which then can be
applied using the above mentioned _subs_atomic method. I believe it
will also be possible to implement this functionality only in Basic
without need to overwrite it in subclasses. A possible name for this
method might be "_subs_pattern".
* It has to be ensured that matching functions only use subs for
atomic patterns - otherwise we will get infinite recursion.
The resulting substitution logic would then look something like the
following:
--------- _subs_pattern (for every item in the sequence if
item has nonatomic pattern)
|
subs ---
|
--------- _subs_atomic (for every item in the sequence if item
has atomic pattern)
I believe this might be a big simplification and clarification of the
current sustitution/matching logic. It might also be adviseable to
factor part of the code out into some sort of "find" function which
does the recursive looking for some pattern and might also come in
handy for other uses.
Please speak up if you have objections, questions, suggestions or just
feel the wish to communicate ;)
Sebastian
--
You received this message because you are subscribed to the Google Groups
"sympy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sympy?hl=en.