Say that I have a huge list (>300.000 elements) of polynomial equations and I want to simplify these by
- looking for some simple equations of the form
x[i] == _or_ == x[i], - converting these equations to a list of rules of the form
x[i] -> pol[i]wherepol[i]is a polynomial that can depend onx[1],...,x[n](but typically only depends on a few variables), and - substituting these rules repeatedly via
ReplaceRepeatedin order to reduce the number of variables in the system.
The problem that typically arises is that some of these rules might be circular. For example from the system
{ x[1] == x[2] * x[3] * x[5], x[4] == x[5] * x[6], x[5] == x[1], ... } I would get a list of rules that looks like
{ x[1] -> x[2] * x[3] * x[5], x[4] -> x[5] * x[6], x[5] -> x[1], ... } Using ReplaceRepeated will run forever.
The question I have is: what would be an efficient way to select the biggest subset of a list of rules of the form x[i] -> pol[i] such that this subset is non-circular, i.e. ReplaceRepeated would not run forever? It may be assumed that any 'self-circular' rule (in which a variable appears both on the LHS and RHS) has already been removed.
I used to have an implementation that looks like
clashingRulesQ[ a_ -> b_, c_ -> d_ ] := With[ { vb = Variables[b], vd = Variables[d] }, Or[ a == c, MemberQ[a] @ vd, MemberQ[c] @ vb ] ] removeCircularRules[ ruleList_ ] := DeleteDuplicates[ ruleList, clashingRulesQ ] but this one removes more rules than necessary since it would, e.g. reduce
{ x[1] -> x[2], x[2] -> x[3] } to
{ x[1] -> x[2] }
x[5]gets replaced byx[1], and $5>1$ so you remove it, butx[4]gets replaced by variablesx[5]*x[6], and $4 <5,6$, and hence you keep it. You could also refine this a bit further and remove, sayx[5] -> x[1]only ifx[1]has a replacement rule. Otherwise, leave it. By the way, your implementation ofremoveCircularRulesseems to leave{x[1] -> x[2] x[3] x[5], x[4] -> x[5] x[6]}for me, not{ x[1] -> x[2] * x[3] * x[5] }. $\endgroup$