# Multi-index variable depending on signature of permutation of indices

I do have some multi-indexed variable, e.g. like this

\begin{align*}f_{123} &= 1\\f_{345} &= 1/2\end{align*}

where $f$ is antisymmetric under permutation of any pair of indices, i.e. e.g. $f_{132}=-1$ and so on. Also, $f_{ijk}=0$ for $(ijk)$ not being a permutation of either $(123)$ or $(345)$. I am wondering what the right "Mathematica way" is to set up such a scenario.

My current attempt:

Define all values depending on the indices

ClearAll[f, f2];
f[{1, 2, 3}] = 1;
f[{3, 4, 5}] = 1/2;


Explicitly set all f to zero if their argument is not {1,2,3} or {3,4,5}.

f[list_ /; list != {1, 2, 3} || {3, 4, 5}] := 0;


Then define an f2 which takes a 3dim list as argument, evaluates the Signature[list] and multiplies with f called with Sorted list:

 f2[list_?(VectorQ[#, NumericQ] &) /; Length[list] == 3] := Signature[list]*f[Sort@list];


This works as I want it to work. However, I feel like there must be another way to accomplish this - and I actually think that my attempt is not the best one (I have a gut feeling that it might miss certain cases and lead to errors at some point).

P.S.: If you do have better suggestions for Tags, I would much appreciate an edit/suggestion.

• I'd have done f[idx_ /; ! MatchQ[Sort[idx], {1, 2, 3} | {4, 5, 6}]] := 0 myself. – J. M. is away Apr 1 '16 at 12:43
• @MartinBüttner Thanks for the suggestion. Of course, it is way more practicable to just use f[_]=0, not thinking straight there :) – Lukas Apr 1 '16 at 12:47
• That depends on your application. Would you want, say, f["stuff"] or f[{1, 2, 3, 4, 5}] to return 0 as well? – J. M. is away Apr 1 '16 at 12:49
• @J.M. That's right. Actually, in the way I need it right now it doesn't matter whether I use f[_]:=0 or the explicitly stated "zero cases". However, it is true that this might cause issues if the code develops. So I will probably stick to explicitly stating when f should be zero and when it should stay unevaluated. – Lukas Apr 1 '16 at 12:55

Since v10.1 you can use also OrderlessPatternSequence like this :

f[x : {OrderlessPatternSequence[1, 2, 3]}] := Signature[x]*1
f[x : {OrderlessPatternSequence[3, 4, 5]}] := Signature[x]*1/2


then

f[{2, 1, 3}]


-1

or

f[{5, 3, 4}]


1/2

• That is really nice! Unfortunately I am still using 10.0.1 and cannot make use of OrderlessPatternSequence. But that seems to a very elegant solution which is also exremely transparent – Lukas Apr 1 '16 at 22:09
• I learned something! – evanb Apr 1 '16 at 23:38
• I decided to accept this answer (although I cannot use it) since it appears to me as the most elegant approach that also differs most from how I did it. – Lukas Apr 5 '16 at 8:48

Not very elegant, but (hopefully) transparent:

The basic data

p1 = {1, 2, 3}; pp1 = Permutations[p1];
p2 = {4, 5, 6}; pp2 = Permutations[p2];


The function

g[x_] := Which[MemberQ[pp1, x], Signature[x], MemberQ[pp2, x],
Signature[x]/2, True, 0]

(* or ending with 1/2] in which case x is returned unevaluated *)


All tuples

t = Tuples[Range[6], 3];
Length[%] == 6^3

(* Out[84]= True *)


Check g on t

{#, g[#]} & /@ t;
Select[%, #[[2]] != 0 &]

(*
{{{1, 2, 3}, 1}, {{1, 3, 2}, -1}, {{2, 1, 3}, -1}, {{2, 3, 1},
1}, {{3, 1, 2}, 1}, {{3, 2, 1}, -1}, {{4, 5, 6}, 1/
2}, {{4, 6, 5}, -(1/2)}, {{5, 4, 6}, -(1/2)}, {{5, 6, 4}, 1/
2}, {{6, 4, 5}, 1/2}, {{6, 5, 4}, -(1/2)}}
*)

• I don't see the sign change anywhere... – J. M. is away Apr 1 '16 at 16:49
• @J.M. oops, you are right, I missed it. Now corrected. BTW your elegant solution can't possibly be beaten. – Dr. Wolfgang Hintze Apr 1 '16 at 17:21
• @Dr.WolfgangHintze Indeed a very transparent solution and I like (from a pedagogic point of view) the combination of Permutations and MemberQ. Thanks for this – Lukas Apr 1 '16 at 21:59

f[{1, 2, 3}] = 1;
f[{3, 4, 5}] = 1/2;
f[list_List /; Not[OrderedQ[list]]] := Signature[list] f[Sort@list]


Only bind the last definition if the argument isn't sorted, in which case sort and use Signature to get the correct sign. Then f[{3,2,1}] yields -1 and f[{4,5,3}] yields 1/2. You can then add f[___]:=0 default, if you want. With that default, f[{1,2,3,4}] and f[{5,1,2,3,4}] yield 0.

f[list_ /; OrderedQ[list] && Not[MemberQ[{{1, 2, 3}, {3, 4, 5}}, list]]] := 0


but this line MUST come after the previous list-reordering definition.

• Also pretty compact solution. Thank you! – Lukas Apr 1 '16 at 22:05
• There might be a smart way to use the DownValues of f to smartly rewrite the last clause in the last line there. – evanb Apr 1 '16 at 23:39

depending on how you use it , it might be good performance-wise to precompute an array:

fa = SparseArray[
Flatten[
Function[{t},
(  # -> t[[2]] & /@ NestList[RotateLeft, t[[1]], 2])] /@
{ {{1, 2, 3},   1},
{{2, 1, 3},  -1},
{{3, 4, 5}, 1/2},
{{4, 3, 5},-1/2} }, 1] (*,{n,n,n}*) ];
(* ^ add dimension spec to handle #>5 *)
f[list_?(VectorQ[#, NumericQ] &) /; Length[list] == 3 &&
Max[list] <= 5] := Extract[fa, list]

f /@ {{1, 2, 3}, {4, 3, 5}, {3, 2, 1},{1,4,5}}


{1, -(1/2), -1, 0}

• That looks pretty compact. However, I do not understand what the NestList[...] part does (and consequently what it is needed for). Could you elaborate on that briefly? – Lukas Apr 1 '16 at 22:02
• evaluate this: NestList[RotateLeft,{1, 2, 3}, 2], it should be clear. – george2079 Apr 1 '16 at 22:27
• True. In what case do you think is the precomputed array favorable? Even for 20 nonzero values, the result is given instantly. So I suspect maybe for more than 3 indices and a few 100 nonzero values? – Lukas Apr 2 '16 at 8:06