Creating a matrix mirrored around it's main diagonal

1,131 views
Skip to first unread message

Tiemo Kieft

unread,
Dec 3, 2009, 7:28:56 AM12/3/09
to clo...@googlegroups.com
Hi,

I'm trying to create a matrix, basically it's a vector of vectors. I copied the code from ants.clj to create the matrix, however, I have to mirror the matrix around it's main diagonal (see below). Currently I'm doing this by first creating the matrix like this:
(apply vector (map (fn [i]
(apply vector (map (fn [j] j) n))) n))))))

Where n is a range. After doing this I basically repeat the same thing, now mirroring the matrix around it's main diagonal. While this works, it's ugly and inefficient, and I'm wondering if there is a better of way of doing this. Below is a diagram of how it should look.

| 1 | 2 | 3 |
----------------
1 | 0 | A | B |
----------------
2 | A | 0 | C |
----------------
3 | B | C | 0 |
----------------

The 0's on the diagonal just mean that the diagonal is ignored, A, B and C are ref's.

- Tiemo.


CuppoJava

unread,
Dec 3, 2009, 9:09:19 AM12/3/09
to Clojure
Matrix arithmetic is one of those cases where I feel indexing is
clearer than filter/map functions.

However, if you're representing matrices as vectors as vectors, this
will likely come with a little bit of extra overhead.

That's not a problem usually because if you plan on doing any sort of
heavy numerical calculations on matrices, the performance you get on
vectors-of-vectors is very poor. So if speed is a concern, you should
represent your matrices as Java arrays in which case the overhead of
indexing goes away.

Here's some example code:

(def matrix [[1 2 3]
[4 5 6]
[7 8 9]])

(def size 3)

(def positions
(for [c (range size)
r (range (inc c) size)]
[r c]))

(reduce (fn [matrix [r c]]
(assoc-in matrix [r c]
(get-in matrix [c r])))
matrix
positions)

Hope that helps
-Patrick

PS: And if you do use Java arrays in the future, you can make the code
much more elegant by replacing the "for" with a "doseq" and eliminate
the need for a "reduce".

ataggart

unread,
Dec 4, 2009, 2:31:07 AM12/4/09
to Clojure
Perhaps I'm misapprehending the issue, but why isn't the following
sufficient:

(defn transpose [matrix]
(vec (apply map vector m)))

(transpose
[[1 2]
[3 4]])

[[1 3]
[2 4]]

Tiemo Kieft

unread,
Dec 4, 2009, 5:27:54 AM12/4/09
to clo...@googlegroups.com
This is what it should look like:

>> | 1 | 2 | 3 |
>> ----------------
>> 1 | 0 | A | B |
>> ----------------
>> 2 | A | 0 | C |
>> ----------------
>> 3 | B | C | 0 |
>> ----------------
>
> Perhaps I'm misapprehending the issue, but why isn't the following
> sufficient:
>
> (defn transpose [matrix]
> (vec (apply map vector m)))
>
> (transpose
> [[1 2]
> [3 4]])
>
> [[1 3]
> [2 4]]

So transposing it is not enough. I need the part above the main diagonal to be 'transposed' and put underneath the main diagonal. Maybe I wasn't as clear as I hoped. Let me restate the issue.

Currently I'm doing this in 2 stages, basically generating a matrix of refs, then copying the upper half into the lower half. The problem is that I want to do this in 1 function, but the code that I posted earlier can't do this, because the map basically has to refer to the matrix that it's trying to build up. I tried doing it like this:

(apply vector (map (fn [i]
(apply vector (map (fn [j] (cond
(= i j) nil
(< i j) (ref somevalue)
(> i j) (nth (nth matrix j) i))) n))) n))))))

But as you'd expect all values under the main diagonal get set to nil, because those 2 nth's are referring to positions in the matrix that hasn't been set yet.

So currently I got it working by basically repeating the above code. It's ugly and inefficient, and I'm wondering whether it can be done more efficiently.

As for performance, I don't actually intend do do any matrix operations on it, just lookups.

- Tiemo.









Konrad Hinsen

unread,
Dec 4, 2009, 6:53:53 AM12/4/09
to clo...@googlegroups.com
On 04.12.2009, at 11:27, Tiemo Kieft wrote:

> So transposing it is not enough. I need the part above the main
> diagonal to be 'transposed' and put underneath the main diagonal.
> Maybe I wasn't as clear as I hoped. Let me restate the issue.

So you want to symmetrize your matrix, right? If possible, I'd
construct it symmetric right from the start, e.g.:

(defn matrix-element
[i j]
(cond (= i j) 0
(< i j) (- j i)
:else (matrix-element j i)))

(def matrix
(vec (for [i (range 5)]
(vec (for [j (range 5)]
(matrix-element i j))))))

Konrad.
Reply all
Reply to author
Forward
0 new messages