3

I am trying to set up a clever set of rules. Say, I am working symbolically with commutators comm of matrices, where for instance

commRules={
    comm[a_+b_,c_]:>comm[a,c]+comm[b,c],
    comm[ac_?NumericQ * a_+bc_?NumericQ * b_,c_]:>ac*comm[a,c]+bc*comm[b,c],
};

Since those two rules are moreorless the same (up to the constants ac and bc), I am pretty sure one can combine them into one rule. I was thinking of using something like default arguments for functions, as for instance done in this question and answer - so maybe something along these lines: comm[ac:(_?NumericQ):1 *a_ + b_,c_]:>ac*comm[a,c]+comm[b,c]. Of course, this does not work and ends up throwing an error.

Is it possible to combine the _?NumericQ tests such that I do not have to hard-code each combination of where the constant factors may appear?

To be very clear, I would like to have a SINGLE rule commRule that does e.g. do the follwing

comm[a+b,c]/.commRule
comm[2*a+b,c]/.commRule
comm[2*a+3*b,4*c]/.commRule
(* comm[a,c]+comm[b,c] *)
(* 2*comm[a,c]+comm[b,c] *)
(* 8*comm[a,c]+12*comm[b,c] *)

** Update ** I also unsuccessfully attempted to use BlankNullSequence for this:

comm[a + b, c] /. {comm[a_ + fac2___?NumericQ*b_, c] :> comm[a, c] + fac2*comm[b, c]}
(* comm[a + b, c] *)
Lukas
  • 2,702
  • 1
  • 13
  • 20
  • 1
    In order to avoid subtle mistakes it is usually better to use a set of simple rules. E.g. commRules={ comm[a_+b_,c_] :> comm[a,c]+comm[b,c], comm[ac_?NumericQ * a_,c_] :> ac*comm[a,c] }; – Ray Shadow Apr 11 '17 at 22:59
  • @Shadowray Thanks for your input. You're suggesting the usage of ReplaceRepeated in order to split all commutators involving sums into the form comm[a,c] and then check for constants, right? This is my current approach actually, but I am curious if a one-liner exists. Can you please be a bit more specific about the subtle mistakes you're referring to? – Lukas Apr 11 '17 at 23:10

1 Answers1

3

Your three examples are met using Optional and the existing Default for Times:

commRule = comm[ac_. a_ + bc_. b_, cc_. c_] :> ac*cc*comm[a, c] + bc*cc*comm[b, c];

comm[a + b, c]       /. commRule
comm[2*a + b, c]     /. commRule
comm[2*a + 3*b, 4*c] /. commRule
comm[a, c] + comm[b, c]

2 comm[a, c] + comm[b, c]

8 comm[a, c] + 12 comm[b, c]

What remains is the additional testing to prevent:

comm[x*a + b, c] /. commRule
comm[b, c] + a comm[x, c]

With that additional testing:

commRule = comm[ac_. a_ + bc_. b_, cc_. c_] /; 
    NumericQ[ac] && NumericQ[bc] && NumericQ[cc] :> 
   ac*cc*comm[a, c] + bc*cc*comm[b, c];

Or in a different style, arguably more or less clean:

commRule = comm[Optional[ac_?NumericQ] a_ + Optional[bc_?NumericQ] b_, 
    Optional[cc_?NumericQ] c_] :> ac*cc*comm[a, c] + bc*cc*comm[b, c];

Now:

comm[x*a + b, c] /. commRule
comm[b, c] + comm[a x, c]

This might not be what is desired, but it results because the expression a x matches the pattern a_ and a default value of 1 is used for ac_. -- you should clarify how this case should be handled.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Thank you very much, your last version of commRule is exactly what I was looking for. That a x matches the pattern a is absolutely desired in this case, unless neither a or x have attribute NumericQ. – Lukas Apr 12 '17 at 08:58
  • @Lukas Glad I could help, and thanks for the Accept. – Mr.Wizard Apr 12 '17 at 16:29