Is there a way to perform Outer operation without evaluating diagonal elements like:

Outer[f, {1, 2, 3}, {1, 2, 3}], expecting the following output:

{{f[1, 2], f[1, 3]}, {f[2, 1], f[2, 3]}, {f[3, 1], f[3, 2]}}?

Shold I put condition on the function f?

Thank you

up vote 6 down vote accepted
Outer[If[# == #2, ## &[], f@##] &, {1, 2, 3}, {1, 2, 3}]

{{f[1, 2], f[1, 3]}, {f[2, 1], f[2, 3]}, {f[3, 1], f[3, 2]}}

Also

Array[If[# == #2, ## &[], f@##] &, {3, 3}]

{{f[1, 2], f[1, 3]}, {f[2, 1], f[2, 3]}, {f[3, 1], f[3, 2]}}

Note: You can also use Unevaluated @ Sequence and, in versions 10+, Nothing in place of ##&[].

Update: An alternative to post-processing suggested by VitaliyKaurov is to re-define f to give Nothing for equal arguments (f[x_,x_]:=Nothing) before using it in Outer:

res1 = Block[{g = f}, g[x_,x_]:=Nothing;Outer[g, Range[10^3], Range[10^3]]  ]; // 
    RepeatedTiming  // First  

0.56

res2 = Replace[Outer[f, Range[10^3], Range[10^3]], _[x_, x_] -> Nothing, {2}]; // 
    RepeatedTiming// First 

0.704

res1 == res2  

True

  • 2
    Nothing instead of ## &[] would work too. – Vitaliy Kaurov Aug 10 at 8:27
  • Thank you @Vitaliy; added that variation. – kglr Aug 10 at 8:32
  • Block[{g = f}, g[x_, x_] :=...] sets a value for f[x_, x_] :=... (because the head of the LHS is evaluated first inside SetDelayed). Strangely Block[{f}, f[x_, x_] :=...] adds significant running time, although it seems equivalent to blocking g. – Michael E2 Aug 10 at 11:42
  • @MichaelE2, good point. I am puzzled too. – kglr Aug 10 at 12:03
  • As this was accepted I assume it's fine, but maybe as a note to future readers: This doesnot remove the diagonal from the output. It removes any elements where the arguments are the same, which might not be the same depending on the second and third argument of Outer. – Lukas Lang Aug 10 at 16:25

MapIndexed and Drop removes the diagonal:

MapIndexed[Drop, Outer[f, Range[3], Range[3]]]
(*  {{f[1, 2], f[1, 3]}, {f[2, 1], f[2, 3]}, {f[3, 1], f[3, 2]}}  *)

It removes the diagonal of any square array, for instance, if the array is generated with an f that is already defined or is a Function:

MapIndexed[Drop, Outer[10 #1 + #2 &, Range[3], Range[3]]]
(*  {{12, 13}, {21, 23}, {31, 32}}  *)

It can be used in the operator form MapIndexed[Drop] @ Outer[..].

It's faster than replacing diagonal elements or defining f[x, x] to evaluate to Nothing.

res1 = Block[{g = f},
     g[x_, x_] := Nothing; 
     Outer[g, Range[10^3], Range[10^3]]]; // RepeatedTiming // First
(*  0.42  *)

Clear[f];  (* needed if res1 is evaluated before res2 *)
res2 = Outer[f, Range[10^3], Range[10^3]], _[x_, x_] -> Nothing, {2}]; //
  RepeatedTiming // First
(*  0.43  *)

res3 = MapIndexed[Drop, Outer[f, Range[10^3], Range[10^3]]]; // 
  RepeatedTiming // First
(*  0.199  *)

res1 === res2 === res3
(*  True  *)

Most of the time for the Drop method is spent generating the array:

Outer[f, Range[10^3], Range[10^3]]; // RepeatedTiming // First
(*  0.184  *)

Note: The current method of using Block[{g = f},...] still defines a value for f[x_, x_], since g evaluates to f in SetDelayed. Failing to clear f before running res2 adds about 50% to the execution time (0.61s). Using Block[{f}, f[x_, x_] :=...] takes about 25% longer (0.506s).

Because operation on diagonal elements is performed anyways you could take an alternative path of removing them post-evaluation. You should ask a question what time is spent on the special construct removing diagonal during evaluation? Perhaps an alternative post-removal would be faster.

Replace[Outer[f, {1, 2, 3}, {1, 2, 3}], _[x_, x_] -> Nothing, {2}]

Timing measures:

Replace[Outer[f, Range[10^3], Range[10^3]], _[x_, x_] -> Nothing, {2}]; // AbsoluteTiming

{0.528235`, Null}

Outer[If[# == #2, Nothing, f@##] &, Range[10^3], Range[10^3]]; // AbsoluteTiming

{1.313204`, Null}

Array[If[# == #2, Nothing, f@##] &, {10^3, 10^3}]; // AbsoluteTiming

{1.41581`, Null}

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.