# Equating matrices (or higher order tensors) element-wise

Say I have two matrices (or, as in my case, higher order tensors) $A,B$, and I want to solve the equation $A=B$. To do so , I need a list of equations that equate entry-wise the elements of $A$ and $B$ (then I can pass this list to Solve or DSolve or whatnot). Is there a simpler way to get this list than

Thread /@ Thread[A == B] // Flatten

Because this only works for matrices and I need to add another Thread for each rank. I'm sure some of you code-golfers will come up with somehting elegant and unreadable...

-

Thread[Flatten /@ (A==B)]

-
this evaluates A==B, then maps Flatten over the result; is this what you intended? (compare Thread[Flatten /@ (a \[Equal] b)] // Trace to the trace of the code I give in my answer to see what I mean) –  acl Mar 19 '12 at 22:26
Yes, to be consistent with what he was doing for matrices –  Rojo Mar 19 '12 at 22:27
Right, I see. He has in mind a symbolic matrix on the left hand side, because this does not work for both numerical (also this is what it seems from his motivation). –  acl Mar 19 '12 at 22:29
Yeah. RM's second solution is the only "bullet-proof" one so far. Your solution doesn't work for arbitrary dimensional tensors unless you manually change the 2 –  Rojo Mar 19 '12 at 22:34
Great. How did I not try this first? –  yohbs Mar 19 '12 at 22:39

Maybe I'm missing something, but Solve will work with any depth of tensor. For example,

A = Table[a[i,j], {i, 2}, {j, 2}];
B = Table[b[i,j], {i, 2}, {j, 2}];

Solve[ A == B, Flatten @ A ]

{{a[1, 1] -> b[1, 1], a[1, 2] -> b[1, 2],
a[2, 1] -> b[2, 1], a[2, 2] -> b[2, 2]}}


The tricky part is usually to extract the variables from the matrices. Since $A$ is completely specified, then Flatten @ A here works.

-

One way to do it that has not been proposed yet is:

Equal @@@ Transpose[Flatten /@ {mat1, mat2}]


This should work regardless of whether it is numeric or symbolic or what the order of the tensor is.

An answer that I had earlier was to exploit the Listable attribute of Times and then replace with Equal (only for symbolic matrices/tensors) as:

mat1 mat2 /. Times -> Equal // Flatten


However, as Rojo pointed out, if the symbolic expressions themselves have Times inside them, the above won't work. The way around is to use his suggestion and create function that has the Listable attribute and does the job:

Flatten@Function[{a, b}, a == b, Listable][mat1, mat2]

-
Now your first one is cuute but daangerous. What if your matrixes have symbolic expressions with Timesinside? –  Rojo Mar 19 '12 at 22:36
Thanks for the answer, but the first one doesn't work for me (even if I replace the comma with an ==) and the second works only for matrices (and not for higher rank tensors) –  yohbs Mar 19 '12 at 22:38
Try with Flatten@Function[{a, b}, a == b, Listable][mat1, mat2] –  Rojo Mar 19 '12 at 22:39
That's what I wanted to suggest and then looked to see if anyone did it and found your answer :-) +1. –  Leonid Shifrin Mar 19 '12 at 23:24

The following method is broken for packed arrays. It works for many functions besides Equal, but some functions such as Equal, Unequal, and SameQ behave unexpectedly with regard to the Listable attribute and packed arrays. I am exploring this further. A far less clean but valid substitute is:

Block[{$}, SetAttributes[$, Listable];
A ~$~ B /.$ -> Equal
]


Another option is to use Listable itself:

{A, B} = RandomInteger[3, {2, 4, 3, 2}];

Block[{Equal},
SetAttributes[Equal, Listable];
A == B
]


{{{False, True}, {False, True}, {False, False}}, {{False, True}, {False, False}, {False, False}}, {{False, False}, {False, True}, {False, False}}, {{False, False}, {False, False}, {False, True}}}

Somewhat more efficient is to use the undocumented function InternalInheritedBlock which behaves as though one did SetAttributes[Equal, Listable] directly, saving an extra step of evaluation that occurs with the method above.

InternalInheritedBlock[{Equal},
SetAttributes[Equal, Listable];
A == B
]

-
I always liked those Block solutions. +1 –  Rojo Mar 20 '12 at 0:00
I never think of block — Is there a reason to choose/performance difference between changing (temporarily) Equal to have the Listable attribute and defining a function that is Listable and does the same like in mine? –  The Toad Mar 20 '12 at 0:02
@R.M A simple test shows that (for some cases at least) my method is more efficient. MapThread is slightly ahead but also less automatic in a way. I think that using InheritedBlock is arguably the cleanest method, because it directly sets the Listable attribute on the active function. –  Mr.Wizard Mar 20 '12 at 0:06
@Mr.Wizard Thanks :) –  The Toad Mar 20 '12 at 0:32
Mr.Wizard, I would also think that your InheritedBlock solution should be the most efficient, elegant, robust, and general solution of all so far. The MapThread one only works for matrices. However, there's a behaviour I'm not understanding... Got a min? –  Rojo Mar 20 '12 at 0:43

For instance:

a = RandomReal[{0, 10}, {4, 4}];
b = RandomReal[{0, 10}, {4, 4}];

MapThread[Equal, {a, b}, 2] // Flatten
(*
{False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False}
*)


The above equates the matrices element by element. To see this, define

c = Array[Subscript[d, #1, #2] &, {4, 4}]


then try the code I gave, ie, MapThread[Equal, {c, b}, 2] and your code, Thread /@ Thread[c == b] // Flatten; they give the same answer. But yours does not work for numeric matrices: Thread /@ Thread[a \[Equal] b] // Flatten errors. To see why, trace what is happening: Thread /@ Thread[a == b] // Trace and you see it first evaluates a==b, then threads. So, if one of the matrices is symbolic, this works OK as the Equal returns unevaluated, then gets threaded; but if both are numeric, the Equal[a,b] evaluates to False, and then there's nothing to thread or flatten.

Thus, your code and Rojo's answer rely on one of the matrices being symbolic so that the Equal[a,b] part remains unevaluated. They don't equate a and b elementwise if they are numeric (not that this matters for what you want to do; I just thought it was worth mentioning).

-
I wonder why MapThread does not support levelspec -1. –  Mr.Wizard Mar 20 '12 at 0:45
@Mr.Wizard So do I... –  acl Mar 20 '12 at 0:47

My personal favourite for this type of task is LogicalExpand (and I can't believe that hasn't been mentioned in the existing 5 answers...)

In[1]:= A = Array[a, {2, 2}];
B = Array[b, {2, 2}];
LogicalExpand[A == B]

Out[3]= b[1, 1] == a[1, 1] && b[1, 2] == a[1, 2] &&
b[2, 1] == a[2, 1] && b[2, 2] == a[2, 2]


This will also work just as simply in the case of the higher-rank tensors that you're interested in.

If you want the output as a list, then you could do a Block hack like:

In[4]:= Block[{And = List}, LogicalExpand[A == B]]

Out[4]= {b[1, 1] == a[1, 1], b[1, 2] == a[1, 2],
b[2, 1] == a[2, 1], b[2, 2] == a[2, 2]}

-
This doesn't work if A == B evaluates, at least not without other methods. But, since this probably doesn't matter for the question at hand, +1. –  Mr.Wizard Mar 20 '12 at 7:27

Since Block didn't work as I intended on packed arrays, here is another idea:

Inner[Equal, Flatten@A, Flatten@B, List]


Again Inner is faster than other options:

A = RandomInteger[15, {140, 130, 120}];
B = RandomInteger[15, {140, 130, 120}];

Inner[Equal, Flatten@A, Flatten@B, List]; //AbsoluteTiming