uncomplicate

rrottier 2017-07-01T06:38:45.407865Z

I tried to post a question on https://groups.google.com/forum/#!forum/uncomplicate but it did not seem to go through. So I will ask it here. I have been following along with the linear algebra refresher posts - but also tried to use neantherthal to solve the systems of linear equations (using sv!). And the answers were different from those in the book. I found that by changing the order of the rows I could get the right answer. I first thought this was because of the underlying lapack implementation but when I used python numpy I always got the same answer as the book independent of the order of the rows.

rrottier 2017-07-01T06:39:00.408571Z

Here are two examples:

rrottier 2017-07-01T06:39:28.409977Z

(let [cs (dge 3 1 [16 8 0])]
  (sv! (dge 3 3 [0 4 1
                 4 0 1
                 1 1 -1])
       cs)
  cs)

rrottier 2017-07-01T06:39:34.410325Z

gives:

rrottier 2017-07-01T06:39:55.411520Z

#RealGEMatrix[double, mxn:3x1, order:column, offset:0, ld:3]

  1.00 
  3.00 
  4.00 

rrottier 2017-07-01T06:40:01.411858Z

but:

rrottier 2017-07-01T06:40:21.412949Z

(let [cs (dge 3 1 [0 8 16])]
  (sv! (dge 3 3 [1 1 -1
                 4 0 1
                 0 4 1])
       cs)
  cs)

rrottier 2017-07-01T06:40:24.413132Z

gives:

rrottier 2017-07-01T06:40:39.413939Z

#RealGEMatrix[double, mxn:3x1, order:column, offset:0, ld:3]

 -9.33 
  2.33 
  4.33 

rrottier 2017-07-01T06:43:07.420459Z

I guess my question is: “Why am I getting different answers?”

2017-07-01T08:27:07.755144Z

@rrottier You swapped the variables instead of the equations in your second example, because you forgot that the default matrix layout is column-oriented. Here's what you intended to do (the answer is also what you'd expect):

2017-07-01T08:29:06.761449Z

(let [cs (dge 3 1 [0 16 8])]
   (sv! (dge 3 3 [1 0 4 
                          1 4 0 
                          -1 1 1])
      cs)
   cs)

2017-07-01T08:30:59.768203Z

the answer is:

2017-07-01T08:31:23.769621Z

#RealGEMatrix[double, mxn:3x1, order:column, offset:0, ld:3]

   1.00 
   3.00 
   4.00 

2017-07-01T08:32:55.775343Z

Now, maybe it is inconvenient to you to work with this "rotated" perspective. No problem, just add the option {:order :row} to dge and the matrix will be row-oriented:

2017-07-01T08:35:31.784833Z

(let [cs (dge 3 1 [0 8 16] {:order :row})]
  (sv! (dge 3 3 [1 1 -1
                 4 0 1
                 0 4 1]
            {:order :row})
       cs)
  cs)

=> #RealGEMatrix[double, mxn:3x1, order:row, offset:0, ld:1]

   1.00 
   3.00 
   4.00 

rrottier 2017-07-01T08:51:40.839164Z

@blueberry Thanks. I did not realise the default matrix layout is column oriented. Thanks for pointing that out.

rrottier 2017-07-01T08:52:24.841614Z

Is this a property of the underlying lapack or is it a neantherthal design decision?

2017-07-01T08:56:26.855342Z

@rrottier It's like the parentheses - after a few hours, you will not notice it :) BTW. matrix string prints out that information, and if I remember well, I mention it quite a few times in the tutorials. Please do this kind of feedback, and, if possible, suggest how to make it more obvious.

2017-07-01T08:56:32.855643Z

both

rrottier 2017-07-01T08:57:23.858427Z

PS. Do I really need to specify {:order :row} on (dge 3 1 ...)? It seems like as it is only one column the orientation should be obvious.

rrottier 2017-07-01T08:58:47.863009Z

Yeah #RealGEMatrix[double, mxn:3x1, order:column, offset:0, ld:3] I did not really parse the type signature, was too focussed on the values 😊

2017-07-01T08:58:50.863175Z

Both LAPACK and neanderthal handle both, but column is the default in literature. In lapack, column is the only option for some operations, but neanderthal finds a way to provide roe even in most of these cases.

rrottier 2017-07-01T08:59:37.866043Z

“column is the default in literature” I thought that row is the default in literature.

2017-07-01T09:00:24.869264Z

You need it even for 3x1 because the LAPACK solver requires matching layout for both matrices.

rrottier 2017-07-01T09:00:45.870782Z

At least the literature that i read for systems of linear equations always represent the 2x + 3y + 4z as [2 3 4]

2017-07-01T09:01:18.872908Z

but it is one vector :) that does not have order

2017-07-01T09:01:49.874827Z

order is a computing detail. your books are math textbooks.

rrottier 2017-07-01T09:02:18.876675Z

Ah… that is very true - I am only now stumbling into the computing stuff.

2017-07-01T09:02:34.877582Z

the first rule of numerical computing is: math book is not enough :)

rrottier 2017-07-01T09:03:14.879936Z

I thought functional programming’s promise is: “You can write it just like the math”

2017-07-01T09:03:22.880436Z

if you ignore that, you'll be heavily bitten by floating point arythmetic.

2017-07-01T09:04:21.883426Z

I do not know about that promise, but It is not possible to fullfil

2017-07-01T09:05:23.886872Z

You can write it whatever you want, provided that you do not need to compute it :)

rrottier 2017-07-01T09:05:38.887839Z

Numpy seems to do some transparent casting then…

rrottier 2017-07-01T09:05:57.888978Z

Because it behaves as row oriented.

rrottier 2017-07-01T09:06:21.890429Z

sorry I should have said “non-transparent”

2017-07-01T09:08:00.896231Z

Neanderthal also supports rows and columns. It's just about the default order, and does not have anything to do with floating point precision. In computing, a*b is not always equal to b*a.

rrottier 2017-07-01T09:08:44.898590Z

I think I just stumbled into the column orientation with the mm function as well. I tried to mutiply: (dge 2 2 [1 2 3 4]) with (dge 2 1 [1 2]) and got a dimension error. Does that make sense?

2017-07-01T09:09:32.901151Z

I could also implement such castings, but that is something i want to avoid since it can shoot you in the foot when you need performance.

2017-07-01T09:09:47.902120Z

no

rrottier 2017-07-01T09:10:05.903262Z

Sorry - just edited it. Got it the wrong way round

2017-07-01T09:10:51.905804Z

that does not ave anything to do with order. 2x1 * 2x2 is not allowed by math rules

2017-07-01T09:11:27.907608Z

btw. neanderthal supports mixing orders in most operations. just not in solvers.

rrottier 2017-07-01T09:11:29.907709Z

RE: The castings - the way you do it is probably better, I agree that you want to be transparent to the underlying implementation. It is just confusing coming form another abstraction

rrottier 2017-07-01T09:13:04.912584Z

I was trying to do this in neantherthal.

2017-07-01T09:13:30.914070Z

post the code an i'll tell you where you made a mistake

2017-07-01T09:13:52.915262Z

this works

rrottier 2017-07-01T09:13:53.915353Z

(let [a (dge 2 2 [1 2 3 4])
      b (dge 1 2 [2 5])]
  (mm a b))

2017-07-01T09:14:31.917283Z

you are trying to multiply 2x2 and 1x2

2017-07-01T09:14:51.918336Z

should be 2 1, not 2 w

2017-07-01T09:15:01.918917Z

not 1 2

rrottier 2017-07-01T09:15:05.919148Z

Sorry same mistake as previous

rrottier 2017-07-01T09:15:25.920411Z

(let [a (dge 2 2 [1 2 3 4])
      b (dge 2 1 [2 5])]
  (mm a b))

2017-07-01T09:15:32.920845Z

column orientation does not require you to rotate the whole world :)

rrottier 2017-07-01T09:15:37.921155Z

Now it works but gives the wrong answer

rrottier 2017-07-01T09:16:07.923015Z

[17
 24]

2017-07-01T09:17:08.926697Z

but if the matrix is column oriented, it transfers the 1d vector [1 2 3 4] into columns.

2017-07-01T09:17:38.928508Z

it should be [1 3 2 4]

rrottier 2017-07-01T09:18:06.930133Z

My head hurts…

2017-07-01T09:18:34.931764Z

look, the only thing you need is this:

rrottier 2017-07-01T09:18:46.932355Z

Ok, I think I understand where all my issues are coming from… I just need to change the way I think about it.

2017-07-01T09:19:02.933326Z

matrix is 2d, vector is 1d

2017-07-01T09:19:28.934835Z

you wand to create a matrix by supplying the data in a vector

2017-07-01T09:20:09.937423Z

read my latest blog post. i wrote the whole blog post about these issues

rrottier 2017-07-01T09:20:56.940177Z

I actually read it.

rrottier 2017-07-01T09:21:39.942606Z

Did not understand the implications of the column orientation though.

2017-07-01T09:22:38.946013Z

you probably do not need to change the way you think about it, you just need to be aware of a few etails.

2017-07-01T09:22:38.946018Z

the use matrices efficiently one?

rrottier 2017-07-01T09:23:54.950039Z

But I get the reasons - in haskell I started playing with dense which uses the same kind of abstraction.

rrottier 2017-07-01T09:25:59.957323Z

Thanks for the help.

2017-07-01T09:26:55.960193Z

What I want, is to lay AA in memory in a way that is efficient, but still easy to work with. Obviously, those two dimensions have to be projected into one. But how? Should it be [1112|2313|1−1−2−6][1112|2313|1−1−2−6], or [121|13−1|11−2|23−6][121|13−1|11−2|23−6]? The answer is: it depends on how your algorithm accesses it most often. Neanderthal gives you both options. When you create any kind of matrix, you can specify whether you want it to be column-oriented (:column, which is the default), or row-oriented (:row). In the following example, we will use CPU matrices from the native namespace. The same options also work for functions that create GPU CUDA matrices (cuda namespace), or OpenCL's GPU and CPU matrices (opencl namespace).

2017-07-01T09:27:03.960575Z

and after that...

2017-07-01T09:28:36.965692Z

Clojure sequence that we used as data source has been read column-by-column.

rrottier 2017-07-01T09:28:39.965828Z

“which is the default” is what I missed.

2017-07-01T09:28:44.966167Z

🙂

2017-07-01T09:29:19.968084Z

But this conversation gives me material for a nice blog post, so thank you.

rrottier 2017-07-01T09:30:16.971621Z

“No worries” as they say in Australia. Thanks for the linear algebra series.

rrottier 2017-07-01T09:30:45.973442Z

I really enjoy it.