Exception thrown when adding 2 vectors using Owl using Owl.Dense.Ndarray.Generic

My code is in : example-owl-vector-add/bin/main.ml at main · shubhamkumar13/example-owl-vector-add · GitHub

when I try to add 2 vectors and I’m getting this error
The dimensions of both the vectors is the same but it’s still the error pops up

❯ dune exec -- ./bin/main.exe
                                   
   C0 
R0  0 
R1  0 

   C0 
R0  1 
R1  1 
Fatal error: exception Owl_exception.INVALID_ARGUMENT("two areas are not equal.")

the version of ocaml I am using is:

❯ ocaml --version
The OCaml toplevel, version 5.2.0

Hello!
I read your code but it seems that it is doing more things that just add two owl vectors/arrays. So, I don’t get what you are trying to do. In theory, adding vectors with owl can be done directly and, in general, it is just one line of code as explained in Owl convention

I don’t know if this can help you, but here there is a small example to add two owl vectors using ndarray generic, in this case the values are drafted from the uniform distribution where the type is defined as Float64. As far as I remember, this add function can be applied to several types (well, I think that they have to be consistent, so you can’t add float32 to float64 unless first you use Generic.cast_s2d for convert one to the other first, and then add together).

open Owl
module Arr = Owl_dense_ndarray_generic
let x = Arr.uniform Bigarray.Float64 [|3|]
let y = Arr.uniform Bigarray.Float64 [|3|] 
let z = Arr.add x y
let () = Arr.(x |> print);;
         Arr.(y |> print);;
         Arr.(z |> print);;

Note that sometimes (depending on the context) you can use the inflix operator + like below (an example with matrix addition):

let m1  = Owl.Mat.of_array [|0.1;1.1;2.1;3.1|] 2 2
let m2  = Owl.Mat.of_array [|0.2;1.2;2.2;3.2|] 2 2
let m3 =  Owl.Mat.(m1 + m2)

let () = Owl.Mat.(m1 |> print);;
         Owl.Mat.(m2 |> print);;
         Owl.Mat.(m3 |> print);;

as you see it is adding float even when using +

1 Like

Thanks for the solution! I am sorry I didn’t explain what my code was doing.

Can you verify if my assumptions are correct because I clearly don’t know what I am doing

I assumed the following function consumes 2 parameters ~coords and ~arr
~coords is a tuple of (<value>, (<row index>, <col index>))
and ~arr is a vector in this case it would be [| 0; 0 |].T

then b expression is a vector with 2 rows and 1 col (that is why I was using [| 1.; 1. |])

and f maps over the arr input and adds b to every vector inside arr

which is defined starting here of dimension 2 x 4

let update_col ~(coords:(('a * (int * int)) list)) arr:('a, 'b) Arr.t =
  let b = Arr.init Bigarray.Float64 [|2; 1|] (fun _ -> 1.) in 
  let f a = 
    Arr.pp_dsnda Format.std_formatter a;
    Arr.pp_dsnda Format.std_formatter b;
    Arr.add a b in
  Mat.map_by_col 0 f arr

I probably misunderstood everything, I’ll use the functions you used, Thanks again for the help :smiley:

Hello @shubhamkumar13,
My main question is mainly what are you trying to do? It seems to me that you want to update a value in Owl ndarray given the coordinates (index of row and index of column), if this is the case below you have a example.

First it defines two matrices, and then it changes in place the values first using the function Arr.set and then the function Mat.set. These two functions have a signature with a unit type destination, so they change values in place. As you can see the syntax in both cases is a bit different (be careful).

module Arr = Owl_dense_ndarray_generic

(* We define two matrices of dimensions 2x2 *)
let m1  = Owl.Mat.of_array [|0.1;1.1;2.1;3.1|] 2 2
let m2  = Owl.Mat.of_array [|0.2;1.2;2.2;3.2|] 2 2

let () = 
(* We print our initial matrices m1 and m2 *)
         Owl.Mat.(m1 |> print);;
         Owl.Mat.(m2 |> print);;
(* This change values of m1 using Arr.set *)
(* coord are given with an array of ints  *)
         Owl.Arr.set m1 [| 0;0 |] 4.0;; 
         Owl.Mat.(m1 |> print);;
(* This change values of m2 using Mat.set *)
(* coord are given just a two integer arguments of the function *)
         Owl.Mat.set m2 0 1 4.0;; 
         Owl.Mat.(m2 |> print);;

In your original post you reported an error like:

Fatal error: exception Owl_exception.INVALID_ARGUMENT("two areas are not equal.")

As far as I know (I am not developer of owl just an user), area refers here to a sub-region (sub-matrix) of an array. So this error may appear when you are trying to copy one region of an array into another-region of an array but their areas are different. An example is when you try to copy a column of a matrix that is (2x2) in a matrix that is (3x3), as the columns have different sizes (areas). I give an example below that has this area error (like the one you reported):

let m1  = Owl.Mat.of_array [|0.1;1.1;2.1;3.1|] 2 2
let m2  = Owl.Mat.of_array [|0.2;1.2;2.2;3.2|] 2 2

let () = Owl.Mat.(m1 |> print);;
         Owl.Mat.(m2 |> print);;
(* This code would give an an error reporting inconsistent areas *)
(* Because m1 is not a matrix with the shape of a column of m2   *)
(* actually here m1 has the shape of m2 but not of its columns.  *)
         Owl.Mat.copy_col_to m1 m2 1;;

The code below shows how to use the function Mat.copy_col_to properly to avoid that kind of error:

let m1  = Owl.Mat.of_array [|0.1;1.1;2.1;3.1|] 2 2
let m2  = Owl.Mat.of_array [|0.2;1.2;2.2;3.2|] 2 2
let m3  = Owl.Mat.of_array [|5.3;6.3|] 2 1 

let () = Owl.Mat.(m1 |> print);;
         Owl.Mat.(m2 |> print);;
         Owl.Mat.(m3 |> print);;
(* Now it is working as areas of the input arrays are compatible *)
(* We copy a matrix m3 that is compatible with the columns of m2 *)
         Owl.Mat.copy_col_to m3 m2 1;;
         Owl.Mat.(m2 |> print);;

Maybe the errors you reported as similar to what I have shown here (note that your function f inside update_col uses b for Arr.add a b but b is not an input of f which make me feel a bit uncomfortable even if that is not the problem, maybe that you can try things like f b a and write Mat.map_by_col 0 (f b) arr)

1 Like

So I was trying to create a function that updates any matrix in a column major form.

The b is hardcoded just for testing so treat this as a mwe and the matrix that I am using is 2 x 4 and since I’m adding a 2 x 1 columnwise I don’t understand how the shape is different.

Thanks!
Ok, now I understand a bit better. The functions to update any matrix are already in owl, so you don’t need to create them (unless you need an specific API). Also, I guess that your function update_col is not using the argument coords in your current implementation.

It seems to me that your function aims to sum a vector to a column of a matrix, here you have an example to do that:

(* add_vec_to_matcol v ma ncol 
   sums vector v to the column of a matrix at position ncol of matrix ma *)
let add_vec_to_matcol v ma ncol =
    let new_col = Owl.Mat.(v + (col ma ncol)) in 
    Owl.Mat.copy_col_to new_col ma ncol 

let m1 = Owl.Mat.of_array [|0.1;1.1;2.1;3.1;4.1;5.1;6.1;7.1|] 2 4
let m3 = Owl.Mat.of_array [|5.3;6.3|] 2 1 

let () = Owl.Mat.(m1 |> print);;
         Owl.Mat.(m3 |> print);;
         add_vec_to_matcol m3 m1 0;;
         Owl.Mat.(m1 |> print);;

If you need named arguments with default values you can introduce changes in this function. Note that you could test the shapes of the vector v and the matrix ma inside the function add_vec_to_matcol to ensure that you don’t have errors of the area.

In languages like Julia there is a convention to add ! at the end of a function if it replaces one of the arguments rather than provide a new instance of a type. In OCaml you can’t add ! but you can add _ (for example) if you like to remember yourself that this function provides change in-place. For example define:

let add_vec_to_matcol_ ma v ncol =
    let new_col = Owl.Mat.(v + (col ma ncol)) in 
    Owl.Mat.copy_col_to new_col ma ncol 

To highlight that this function is updating ma (the first argument of the function). I recall that this new version should be used as add_vec_to_matcol_ m1 m3 0 in the example above.

1 Like

Thanks for the detailed help!

I have one last tiny question.

I see that you’ve use Owl.Mat.of_array to create matrix and vector.

What if I want to create a matrix that can take any type of input?

I used Owl.Dense.Ndarray.Generic.create to create one and Owl.Dense.Ndarray.Generic.init in another function to do the same thing albeit a little differently.

What did I get wrong when I was using this function?

I think there is nothing wrong with using other functions you mentioned to create a matrix, below you have several examples (but check also about slicing/broadcasting if you need to do more fancy things):

module Arr = Owl_dense_ndarray_generic
module Mat = Owl_dense_matrix_generic

let finit_test1 offset pos = (float_of_int pos) +. offset
let finit_test2 scale  pos = (float_of_int pos) *. scale 

(* Examples of create and init of Owl_dense_matrix_generic *) 
let m1 = Mat.create Bigarray.Float64 2 4 50.1
let m2 = Mat.init Bigarray.Float64 2 4 (finit_test1 0.0)  
let m3 = Mat.init Bigarray.Float64 2 4 (finit_test1 0.5) 

(* Examples of create and init of Owl_dense_ndarray_generic *) 
let m4 = Arr.create Bigarray.Float64 [| 2;4 |] 31.1
let m5 = Arr.init Bigarray.Float64 [| 2;4 |] (finit_test2 2.0 ) 

(* Important: here the functions used by init are processing 
   the index as a 1 dimensional array (you can understand it 
   when you read the results of print m2 below). But if you need
   a function that process values per matrix indices (row, column)
   you may use the function Arr.i1d and Arr.ind for that task, 
   with Arr = Owl_dense_ndarray_generic as defined above *)

(* For Matrix you can also use the init_2d as below *)

let finit_test3 scale dim1 dim2 =
    ((float_of_int dim1) -. (float_of_int dim2)) *. scale

let m6 = Mat.init_2d Bigarray.Float64 2 4 (finit_test3 0.5) 
let m7 = Mat.init_2d Bigarray.Float64 3 3 (finit_test3 2.0)
 
let () = Owl.Mat.(m1 |> print);;
         Owl.Mat.(m2 |> print);;
         Owl.Arr.(m3 |> print);;
         Owl.Arr.(m4 |> print);;
         Owl.Arr.(m5 |> print);;
         Owl.Arr.(m6 |> print);;
         Owl.Arr.(m7 |> print);;

Finally note if for any reason you would need to rely on the standard library only, there is there also a basic support for matrices (you could see small examples of using Array standard library here or here)

1 Like

Thanks :heart:

I really appreciate helping me out and spelling out the explanation in your spare time, this is going to be extremely helpful.

1 Like