27
$\begingroup$

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...

$\endgroup$

7 Answers 7

15
$\begingroup$

What about flattening first?

Thread[Flatten /@ (A==B)]
$\endgroup$
6
  • $\begingroup$ 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) $\endgroup$
    – acl
    Commented Mar 19, 2012 at 22:26
  • $\begingroup$ Yes, to be consistent with what he was doing for matrices $\endgroup$
    – Rojo
    Commented Mar 19, 2012 at 22:27
  • 1
    $\begingroup$ 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). $\endgroup$
    – acl
    Commented Mar 19, 2012 at 22:29
  • $\begingroup$ 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 $\endgroup$
    – Rojo
    Commented Mar 19, 2012 at 22:34
  • $\begingroup$ Great. How did I not try this first? $\endgroup$
    – yohbs
    Commented Mar 19, 2012 at 22:39
10
$\begingroup$

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 Internal`InheritedBlock which behaves as though one did SetAttributes[Equal, Listable] directly, saving an extra step of evaluation that occurs with the method above.

Internal`InheritedBlock[{Equal},
  SetAttributes[Equal, Listable];
  A == B
]
$\endgroup$
12
  • $\begingroup$ I always liked those Block solutions. +1 $\endgroup$
    – Rojo
    Commented Mar 20, 2012 at 0:00
  • $\begingroup$ 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? $\endgroup$
    – rm -rf
    Commented Mar 20, 2012 at 0:02
  • $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Commented Mar 20, 2012 at 0:06
  • $\begingroup$ @Mr.Wizard Thanks :) $\endgroup$
    – rm -rf
    Commented Mar 20, 2012 at 0:32
  • $\begingroup$ 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? $\endgroup$
    – Rojo
    Commented Mar 20, 2012 at 0:43
10
$\begingroup$

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]}
$\endgroup$
1
  • 2
    $\begingroup$ 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. $\endgroup$
    – Mr.Wizard
    Commented Mar 20, 2012 at 7:27
9
$\begingroup$

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.

$\endgroup$
7
$\begingroup$

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]
$\endgroup$
6
  • $\begingroup$ Now your first one is cuute but daangerous. What if your matrixes have symbolic expressions with Timesinside? $\endgroup$
    – Rojo
    Commented Mar 19, 2012 at 22:36
  • $\begingroup$ 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) $\endgroup$
    – yohbs
    Commented Mar 19, 2012 at 22:38
  • 2
    $\begingroup$ Try with Flatten@Function[{a, b}, a == b, Listable][mat1, mat2] $\endgroup$
    – Rojo
    Commented Mar 19, 2012 at 22:39
  • $\begingroup$ That's what I wanted to suggest and then looked to see if anyone did it and found your answer :-) +1. $\endgroup$ Commented Mar 19, 2012 at 23:24
  • 1
    $\begingroup$ 3 years late, but Transpose[Flatten /@ {mat1, mat2}] is equivalent to Flatten[{mat1, mat2}, {{2, 3}, {1}}]. :) $\endgroup$ Commented Jan 29, 2016 at 14:32
5
$\begingroup$

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).

$\endgroup$
2
  • $\begingroup$ I wonder why MapThread does not support levelspec -1. $\endgroup$
    – Mr.Wizard
    Commented Mar 20, 2012 at 0:45
  • $\begingroup$ @Mr.Wizard So do I... $\endgroup$
    – acl
    Commented Mar 20, 2012 at 0:47
5
$\begingroup$

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

MapThread[Equal, {A, B}, 3]; //AbsoluteTiming

Equal @@@ Transpose[Flatten /@ {A, B}]; //AbsoluteTiming

{0.3010172, Null}

{0.4230241, Null}

{0.4220242, Null}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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