# Linear function on strings using UpValues

I have two functions Sup and Sdown that take a string of letters with allowed characters "Z","W","P","H" and returns a "sum" of strings with signs:

Sup[simple_String]:=
Block[{char=Characters[simple],z=0,w=0,n},
n=Count[char,"Z"|"P"];
Sum[
Which[
char[[i]]=="P",
z++;w++;0,
char[[i]]=="W",
w++;0,
char[[i]]=="Z",
z++;
Sigg[n,z,w]*StringJoin@@(Join[char[[;;i-1]],{"W"},char[[i+1;;]]]),
True,
0
],{i,Length[char]}
]
]


and

Sdown[simple_String]:=
Block[{char=Characters[simple],z=0,w=0,n},
n=Count[char,"Z"|"P"];
Sum[
Which[
char[[i]]=="P",
z++;w++;0,
char[[i]]=="Z",
z++;0,
char[[i]]=="W",
w++;
Sigg[n,z+1,w]*StringJoin@@(Join[char[[;;i-1]],{"Z"},char[[i+1;;]]]),
True,
0
],{i,Length[char]}
]
]


The sign is simply given by

Sigg[n_,z_,w_]:=If[EvenQ[n+w-z],1,-1]


As expected, this gives me i.e.

In= Sdown["WPWPHH"]
Out= -"WPZPHH" + "ZPWPHH"


Now, I want Sup and Sdown to be linear operators that can act on stuff like Out, so I do this:

Sup/:Sup[Plus[x__]]:=Plus@@(Sup/@x)

Sup/:Sup[Times[i_Integer,s_String]]:=Expand[i*Sup[s]]


and the same for Sdown. But when I try to Sup[Sdown["WPWPHH"]] I get

In= Sup[Sdown["WPWPHH"]]
Out= 2 + "WPWPHH"


instead of 2"WPWPHH" as I would expect. The FullForm of Out is Plus[Times[-1,"WPZPHH"],"ZPWPHH"] so when I do

In= Plus @@ (Sup /@ {Times[-1, "WPZPHH"], "ZPWPHH"})


I get

Out= 2 "WPWPHH"


like expected. What's going on here? Can you help me find the error? Also, if there is a better way of implementing this linearity, please let me know :)

Thank you for a well-written question with complete code that made this reasonable to answer. Welcome to Mathematica Stack Exchange. :-)

1. You do not need UpValues definitions here. That would only apply if you were attempting to add a rule to e.g. Plus rather than Sup, yet your use of TagSetDelayed makes it clear that you are attaching the rule to Sup.

2. The major remaining issue is the pattern Plus[x__] which will not work; see: Why is ReplaceAll behaving like this?

3. In the pattern above, even if it did work, x__ would be a sequence of expressions; you would need to wrap it in something before mapping, e.g. a List: Plus @@ Sup /@ {x}

Fortunately Map will work on expressions of arbitrary head therefore both problems can be avoided by matching the entire Plus object rather than the sequence. Your solution is therefore:

Sup[x_Plus] := Sup /@ x


Now:

Sup[Sdown["WPWPHH"]]

2 "WPWPHH"

• Thank you for the quick answer. I now have Sup[x_Plus]:=Sup/@x and Sup[i_Integer*s_String]:=i*Sup[s] and it works brilliantly. – Marius Ladegård Meyer Nov 13 '14 at 10:41
• @Marius Glad I could help. Another formulation you might consider is: Sup[i_Integer] := i; Sup[x : _[__]] := Sup /@ x. This maps Sup over any compound expression. Atoms such as Strings or Integers are then individually handled. – Mr.Wizard Nov 13 '14 at 12:16
• Will there be any benefits (time, memory, ...) other than conceptual clarity in this case? In any case, thanks for the tip, it will surely help me with some of my other linear functions. – Marius Ladegård Meyer Nov 13 '14 at 12:23
• @Marius I don't know without testing. On the surface I wouldn't expect any large performance difference but I have been surprised before. – Mr.Wizard Nov 13 '14 at 12:27