# Cyclically Flat Functions

Any idea how to define some operator ° such that e.g. $((a°b)°c)°d=a°(b°(c°d))=a°b°c°d=b°c°d°a$ (but $a°b\neq{b°a}$ in general)? So ° is Flat plus (insert here)?

• If I define $e=b \circ c \circ d$ then you propose $a \circ e \neq e \circ a$, but on the other hand you say $a \circ (b \circ c \circ d) = a \circ b \circ c \circ d = b \circ c \circ d \circ a = (b \circ c \circ d) \circ a = e \circ a$. If $\circ$ is flat and cyclic then mustn't $a \circ e = e \circ a$...? Jun 4 '17 at 19:04
• @MariusLadegårdMeyer, I think that a non-commutative "operator" with the requested properties could be Tr[A.B.C] where the arguments are matrices. This is only defined for matrices whose sizes agree. With two arguments it is commutative, but with three or more, generally not. Jun 4 '17 at 19:16
• Yes, it's indeed a tensor trace in "reality". Jun 5 '17 at 10:41
• Somewhat related: (17041), (55702) Jun 6 '17 at 0:03
• Tensor trace cannot be Flat. Just try to imagine Tr[a,Tr[b,c]] == Tr[a,b,c]. To define tensor trace fully consistent you will need two functions, say, tr and dot, where dot is Flat and tr[dot[x__]] is cyclic. How to implement such functions depends on what kind of patterns do you want to use. If you want to combine tr and dot in one symbol, you should not expect it to be Flat. Jun 6 '17 at 6:36

Updated to fix cyclic sort, and to allow additional operator definitions

@mikado mentions that giving an operator the Flat attribute in addition to a cyclic automatic sort makes the operator commutative. This is because the cyclic automatic sort gets applied during the Flat pattern matching rewrite. It is possible to avoid this issue as follows:

ClearAll[SmallCircle];
SetAttributes[SmallCircle,Flat];

u_SmallCircle:= Module[{args = Hold @@ Unevaluated @ u, ord},
ord = First @ Ordering @ NestList[RotateLeft, args, Length[args] - 1];
RotateLeft[Unevaluated[u], ord-1] /; ord != 1
]


The key is that SmallCircle[u__] is never used, and so Flat pattern matching rewrites of the sort:

SmallCircle[a, b, c] -> SmallCircle[SmallCircle[a, b], c]


never occur. Simple example:

a ∘ c ∘ b
e ∘ c ∘ b


a∘c∘b

b∘e∘c

Update

The OP requested to have support for additional SmallCircle definitions. The problem with adding additional SmallCircle rules is that inevitably these additional rules will cause the operator to become commutative. One can either use mikado's approach, or one can add a helper function to take care of these additional rules. For example:

ClearAll[SmallCircle, iSmallCircle];
SetAttributes[{SmallCircle, iSmallCircle}, Flat];

u_SmallCircle := Module[{args = Hold @@ Unevaluated @ u, r},
r = cycleSort[iSmallCircle @@ args];
SmallCircle @@ r /; r =!= args
]

iSmallCircle[b, a] := d
iSmallCircle[a, x___, b] := iSmallCircle[d, x]

cycleSort[u_iSmallCircle] := RotateLeft[
Hold @@ u,
First @ Ordering @ NestList[RotateLeft, u, Length[u] - 1] - 1
]


A couple examples:

e ∘ b ∘ a ∘ x ∘ b ∘ a
a ∘ c ∘ b


d∘e∘d∘x

c∘d

• Now suppose that in your example I have an additional simplification rule, say b°a=d. Under which condition will Mathematica "find" it, i.e. try out a°c°b=b°a°c and then simplify to d°c? (And not being stuck at the canonical order a°c°b) Jun 5 '17 at 10:40
• @HaukeReddmann See edit Jun 5 '17 at 22:49
• It's tricky. :-) (I also already thought about if the pattern a(...)b would be sufficient to take care of a b°a in the "wrong" place.) THX. Jun 6 '17 at 8:45

The following definitions give what you request. I use a definition of can1 (from Szabolcs) to canonicalise the order of the arguments.

SmallCircle[a___, SmallCircle[b___], c___] := SmallCircle[a, b, c]
can1 = RotateLeft[#, Ordering[#, 1] - 1] &;
SmallCircle[u__] /; First[Ordering[{u}, 1]] != 1 := SmallCircle @@ can1[{u}]


This gives

{((a∘b)∘c)∘d, b∘c∘d∘a, a∘c∘b∘d}
(* {a∘b∘c∘d, a∘b∘c∘d, a∘c∘b∘d} *)


Note that we can't use SetAttribute Flat as this effectively makes the operator commutative (it lets Mathematica permute any subsequence)

• Flat does not make the operator commutative. Maybe you're thinking of Orderless? Jun 4 '17 at 21:54
• @CarlWoll quite correct. However, if I give SmallCircle the attribute Flat (in conjunction with the 2nd definition) it becomes commutative (try it if you don't believe me). For this reason, I have to give a slightly weaker version of flattening in the 1st definition. Jun 4 '17 at 22:05
• I see. That's because you use SmallCircle[u__] .. in your definition. I'll give an alternate answer that doesn't suffer from this issue. Jun 4 '17 at 22:12