5

I'm trying to find a way (idiomatic or otherwise) to transform a matrix that looks like

0 1
0 1
0 1

into 3 individual matrices

0 1
0 0
0 0

0 0
0 1
0 0

0 0
0 0
0 1

so that when I OR all of them together, I get the original. Each of these "sub-matrices" have to have 1 non-zero element only and must have the same shape as the original.

Chris Zhang
  • 968
  • 7
  • 16

3 Answers3

3

One solution:

Any boolean matrix:

      m←4 3⍴?12⍴2
      m
0 0 1
0 0 0
1 1 0
0 1 0

Note its shape:

    d←⍴m
    d
4 3

Ravel the matrix into a vector:

      v←,m
      v
0 0 1 0 0 0 1 1 0 0 1 0

Generate indices:

          i ←⍳⍴v
          i
    0 1 2 3 4 5 6 7 8 9 10 11

Construct a matrix for each 1 in the original matrix:

      a←d∘⍴¨↓(v/i)∘.=i
      a
 0 0 1  0 0 0  0 0 0  0 0 0 
 0 0 0  0 0 0  0 0 0  0 0 0 
 0 0 0  1 0 0  0 1 0  0 0 0 
 0 0 0  0 0 0  0 0 0  0 1 0 

Verify result:

   ↑∨/a
0 0 1
0 0 0
1 1 0
0 1 0

There is probably a nice way to do this using scatter point indexing as well, by first generating a 3 dimensional matrix and then specifying the location of the 1s.

Yes there is, using v and d as above:

       n←+/v
       b←(n,d)⍴0
       b[↓⍉(⍳n)⍪d⊤v/⍳⍴v]←1
       b
0 0 1
0 0 0
0 0 0
0 0 0

0 0 0
0 0 0
1 0 0
0 0 0

0 0 0
0 0 0
0 1 0
0 0 0

0 0 0
0 0 0
0 0 0
0 1 0
      ∨⌿b
0 0 1
0 0 0
1 1 0
0 1 0
Paul Mansour
  • 1,436
  • 9
  • 11
3

Given a vector A:

+A←3 4⍴1 0 1 0 1 0 0 0 0 1 0 1

1 0 1 0
1 0 0 0
0 1 0 1

Break it down into component matrices as follows:

+(⍴A)∘⍴¨⊂[2](,A)\B B⍴1,(B←+/,A)⍴0

1 0 0 0   0 0 1 0   0 0 0 0   0 0 0 0   0 0 0 0 
0 0 0 0   0 0 0 0   1 0 0 0   0 0 0 0   0 0 0 0 
0 0 0 0   0 0 0 0   0 0 0 0   0 1 0 0   0 0 0 1

How it works

First assign the number of 1s to B:

B←+/,A          ⍝ 5

Create an identity matrix as discussed in this post: The most idiomatic way of creating identity matrix in APL:

B B⍴1,(B←+/,A)⍴0

1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1

Ravel the original matrix:

,A              ⍝ 1 0 1 0 1 0 0 0 0 1 0 1

Use the raveled matrix to expand the identity matrix. That creates a matrix wherein each row is the raveled form of the component matrices:

+(,A)\B B⍴1,(B←+/,A)⍴0

1 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 1

Convert that matrix into a vector of the rows:

+⊂[2](,A)\B B⍴1,(B←+/,A)⍴0

1 0 0 0 0 0 0 0 0 0 0 0  0 0 1 0 0 0 0 0 0 0 0 0  0 0 0 0 1 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0 0 1 0 0  0 0 0 0 0 0 0 0 0 0 0 1

Using the original shape of A (⍴A), create the final matrices:

(⍴A)∘⍴¨⊂[2](,A)\B B⍴1,(B←+/,A)⍴0

1 0 0 0   0 0 1 0   0 0 0 0   0 0 0 0   0 0 0 0 
0 0 0 0   0 0 0 0   1 0 0 0   0 0 0 0   0 0 0 0 
0 0 0 0   0 0 0 0   0 0 0 0   0 1 0 0   0 0 0 1
Community
  • 1
  • 1
Expedito
  • 7,771
  • 5
  • 30
  • 43
  • Very nice solution! I like this one because it is just reshaping the 1's and 0's, and aside from the count of the number of 1's needs no integers or indices. I had thought there must be some way to do it this way and toyed with expansion, but generating the identity matrix did not occur to me. Very nice indeed! – Paul Mansour Jul 17 '13 at 13:29
  • @PaulMansour - Thanks, Paul! I liked your solution too, but I'm little stumped by it because I'm getting a valence error with `↓(v/i)∘.=i`. Shouldn't there be an argument to the left of that? – Expedito Jul 17 '13 at 14:16
  • 1
    Monadic down arrow is known as "split" and does the same thing as ⊂[2], converting a matrix into a vector of vectors. May not be available in all APL implementions. – Paul Mansour Jul 17 '13 at 15:56
  • 1
    @PaulMansour - That's probably the case since I'm using NARS2000. I'll try it with `⊂[2]`. Thanks for the response. – Expedito Jul 17 '13 at 16:03
1

With Dyalog APL version 16.0 you can now write this very succinctly with the tacit function ⍸{~@(⊂⍺)≠⍨⍵}⍤0 2⊢. See the bottom of this post for how to get a list of matrices instead.

Try it online!

How does it work?

It is a fork, where so the results of and of are the left and right arguments to {~@(⊂⍺)≠⍨⍵}⍤0 2.

 gives a list of indices where there are 1s in the argument

 just yields the unmodified argument

{}⍤0 2 applies the following dfn with each rank-0 subarray of the left argument (i.e. each index of a 1) as left argument and each rank-2 subarray of the right argument (i.e. the entire matrix) as right argument

≠⍨⍵unequal selfie of the right argument; gives a matrix of same shape but filled with zeros

~@(⊂⍺) this flips (logical not) the bit at the position indicated by the left argument

So, for each 1, it creates an all-zero layer where the bit in its position is flipped.


How to get a list of matrices instead:

⍸{~@(⊂⍺)≠⍨⍵}¨⊂ gives a list of the desired matrices. Try it online!

This works identically, but instead of using the rank operator to operate on the entire array, we use ¨⊂ to pair each index of a 1 with the entire matrix.

Adám
  • 6,573
  • 20
  • 37