10
1
mirror of https://gitlab.com/scemama/QCaml.git synced 2024-11-07 22:53:41 +01:00

Improved sparse vectors

This commit is contained in:
Anthony Scemama 2019-02-28 15:50:00 +01:00
parent 5afd6f8f82
commit 7a44c8bf64
3 changed files with 91 additions and 106 deletions

View File

@ -6,7 +6,7 @@ let make
?guess ?guess
?(n_states=8) ?(n_states=8)
?(n_iter=10) ?(n_iter=10)
?(threshold=1.e-8) ?(threshold=1.e-6)
diagonal diagonal
matrix_prod matrix_prod
= =
@ -24,16 +24,16 @@ let make
let random_vectors = let random_vectors =
let random_vector k = let random_vector k =
Vec.init n (fun i -> Vec.init n (fun i ->
let r1 = Random.float 1.
and r2 = Random.float 1.
in
let a = sqrt (-2. *. log r1)
and b = Constants.two_pi *. r2
in
if i<k then 0. if i<k then 0.
else if i>k then else if i=k then 1.e5
a *. cos b else
else 100.0 let r1 = Random.float 1.
and r2 = Random.float 1.
in
let a = sqrt (-2. *. log r1)
and b = Constants.two_pi *. r2 in
let c = a *. cos b in
if abs_float c > 1.e-1 then c else 0.
) )
|> Util.normalize |> Util.normalize
in in
@ -73,7 +73,7 @@ let make
matrix_prod ( matrix_prod (
u_new_ortho u_new_ortho
|> Mat.of_col_vecs_list |> Mat.of_col_vecs_list
|> Matrix.dense_of_mat ) |> Matrix.sparse_of_mat )
|> Matrix.to_mat |> Matrix.to_mat
|> Mat.to_col_vecs_list |> Mat.to_col_vecs_list
in in

View File

@ -2,10 +2,16 @@ open Lacaml.D
let epsilon = Constants.epsilon let epsilon = Constants.epsilon
type index_value =
{
index: int;
value: float
}
type sparse_vector = type sparse_vector =
{ {
n: int; n: int;
v: (int*float) list v: index_value list
} }
type t = type t =
@ -23,13 +29,22 @@ let is_dense = function
| Dense _ -> true | Dense _ -> true
exception Found of float
let get = function let get = function
| Dense v -> (fun i -> v.{i}) | Dense v -> (fun i -> v.{i})
| Sparse { n ; v } -> (fun i -> | Sparse { n ; v } -> (fun i ->
if i < 1 || i > n then invalid_arg "index out of bounds"; if i < 1 || i > n then invalid_arg "index out of bounds";
match List.assoc_opt i v with try
| Some x -> x List.iter (fun {index ; value} ->
| None -> 0. ) if index=i then
raise (Found value)) v;
raise Not_found
with
| Not_found -> 0.
| Found x -> x
)
let dim = function let dim = function
@ -47,14 +62,14 @@ let sparse_of_dense ?(threshold=epsilon) = function
if abs_float x < threshold then if abs_float x < threshold then
aux accu (i-1) aux accu (i-1)
else else
aux ((i, x)::accu) (i-1) aux ({index=i ; value=x}::accu) (i-1)
in in
let n = Vec.dim v in let n = Vec.dim v in
Sparse { n ; v=aux [] n } Sparse { n ; v=aux [] n }
let rec to_assoc_list ?(threshold=epsilon) = function let rec to_assoc_list ?(threshold=epsilon) = function
| Sparse {n ; v} -> v | Sparse {n ; v} -> List.map (fun {index ; value} -> (index, value)) v
| Dense v -> to_assoc_list @@ sparse_of_dense ~threshold (Dense v) | Dense v -> to_assoc_list @@ sparse_of_dense ~threshold (Dense v)
@ -62,7 +77,7 @@ let dense_of_sparse = function
| Dense _ -> invalid_arg "Expected a sparse vector" | Dense _ -> invalid_arg "Expected a sparse vector"
| Sparse {n ; v} -> | Sparse {n ; v} ->
let v' = Vec.make0 n in let v' = Vec.make0 n in
List.iter (fun (i, x) -> v'.{i} <- x) v; List.iter (fun {index ; value} -> v'.{index} <- value) v;
Dense v' Dense v'
@ -74,7 +89,10 @@ let sparse_of_vec ?(threshold=epsilon) v =
|> sparse_of_dense ~threshold |> sparse_of_dense ~threshold
let sparse_of_assoc_list n v = Sparse { n ; v } let sparse_of_assoc_list n v =
Sparse { n ;
v = List.map (fun (index, value) -> {index ; value}) v
}
let rec to_vec = function let rec to_vec = function
@ -85,67 +103,22 @@ let rec to_vec = function
let scale ?(threshold=epsilon) x = function let scale ?(threshold=epsilon) x = function
| Dense v -> let v' = copy v in (scal x v'; Dense v') | Dense v -> let v' = copy v in (scal x v'; Dense v')
| Sparse {n ; v} -> Sparse {n ; v=List.map (fun (i,y) -> let z = x *. y in | Sparse {n ; v} ->
if abs_float z > threshold then Some (i, z) else None ) v |> Util.list_some } Sparse {n ; v=List.map (fun {index ; value} ->
let z = x *. value in
if abs_float z > threshold then
Some {index ; value=z}
else
None
) v |> Util.list_some }
let rec neg = function let rec neg = function
| Dense v -> Dense (Vec.neg v) | Dense v -> Dense (Vec.neg v)
| Sparse {n ; v} -> Sparse {n ; v=List.map (fun (i,y) -> (i, -. y)) v} | Sparse {n ; v} ->
Sparse {n ; v=List.map (fun {index ; value} -> {index ; value = -. value}) v}
(*
let rec add ?(threshold=epsilon) x y =
if dim x <> dim y then
invalid_arg "Inconsistent dimensions";
match x, y with
| Dense x , Dense y -> Dense (Vec.add x y)
| Sparse {n ; v}, Dense y ->
let v' = copy y in
List.iter (fun (i, x) -> v'.{i} <- v'.{i} +. x) v;
sparse_of_vec ~threshold v'
| Sparse {n ; v}, Sparse {n=n' ; v=v'} ->
begin
let rec aux accu v1 v2 =
match v1, v2 with
| [], [] -> {n ; v=List.rev accu}
| ((i, x)::v1), [] ->
aux ((i, x)::accu) v1 []
| [], ((j, y)::v2) ->
aux ((j, y)::accu) [] v2
| ((i, x)::v1), ((j, y)::v2) ->
if i = j then
begin
let z = x +. y in
if abs_float z > threshold then
aux ((i, (x +. y))::accu) v1 v2
else
aux accu v1 v2
end
else if i < j then
begin
if abs_float x > threshold then
aux ((i, x)::accu) v1 ((j, y)::v2)
else
aux accu v1 ((j, y)::v2)
end
else
begin
if abs_float y > threshold then
aux ((j, y)::accu) ((i, x)::v1) v2
else
aux accu ((i, x)::v1) v2
end
in
Sparse (aux [] v v')
end
| x, y -> add ~threshold y x
let sub ?(threshold=epsilon) x y = add ~threshold x (neg y)
*)
let axpy ?(threshold=epsilon) ?(alpha=1.) x y = let axpy ?(threshold=epsilon) ?(alpha=1.) x y =
if dim x <> dim y then if dim x <> dim y then
@ -156,47 +129,51 @@ let axpy ?(threshold=epsilon) ?(alpha=1.) x y =
| Sparse {n ; v}, Dense y -> | Sparse {n ; v}, Dense y ->
begin begin
let v' = copy y in let v' = copy y in
List.iter (fun (i, x) -> v'.{i} <- v'.{i} +. alpha *. x) v; List.iter (fun {index ; value} -> v'.{index} <- v'.{index} +. alpha *. value) v;
sparse_of_vec ~threshold v' sparse_of_vec ~threshold v'
end end
| Dense x , Sparse {n ; v} -> | Dense x , Sparse {n ; v} ->
begin begin
let v' = copy x in let v' = copy x in
scal alpha v'; scal alpha v';
List.iter (fun (i, y) -> v'.{i} <- v'.{i} +. y) v; List.iter (fun {index ; value} -> v'.{index} <- v'.{index} +. value) v;
sparse_of_vec ~threshold v' sparse_of_vec ~threshold v'
end end
| Sparse {n ; v}, Sparse {n=n' ; v=v'} -> | Sparse {n ; v}, Sparse {n=n' ; v=v'} ->
begin begin
let rec aux accu v1 v2 = let rec aux accu v1 v2 =
match v1, v2 with match v1, v2 with
| [] , [] -> {n ; v=List.rev accu} | ({index=i ; value=x}::r1), ({index=j ; value=y}::r2) ->
| ((i, x)::v1), [] -> aux ((i, x)::accu) v1 [] begin
| [] , ((j, y)::v2) -> aux ((j, y)::accu) [] v2 match compare i j with
| ((i, x)::v1), ((j, y)::v2) -> | -1 ->
if i = j then
begin
let z = alpha *. x +. y in
if abs_float z > threshold then
aux ((i, z)::accu) v1 v2
else
aux accu v1 v2
end
else if i < j then
let z = alpha *. x in let z = alpha *. x in
begin let new_accu =
if abs_float z > threshold then if abs_float z > threshold then
aux ((i, z)::accu) v1 ((j, y)::v2) {index=i ; value=z} :: accu
else else
aux accu v1 ((j, y)::v2) accu
end in aux new_accu r1 v2
else | 1 ->
begin let new_accu =
if abs_float y > threshold then if abs_float y > threshold then
aux ((j, y)::accu) ((i, x)::v1) v2 {index=j ; value=y} :: accu
else else
aux accu ((i, x)::v1) v2 accu
end in aux new_accu v1 r2
| 0 ->
let z = alpha *. x +. y in
let new_accu =
if abs_float z > threshold then
{index=i ; value=z} :: accu
else
accu
in aux new_accu r1 r2
| _ -> assert false
end
| ({index=i ; value=x}::r1), [] -> aux ({index=i ; value=x}::accu) r1 []
| [] , ({index=j ; value=y}::r2) -> aux ({index=j ; value=y}::accu) [] r2
| [] , [] -> {n ; v=List.rev accu}
in in
Sparse (aux [] v v') Sparse (aux [] v v')
end end
@ -210,7 +187,7 @@ let pp_vector ppf = function
| Sparse {n ; v} -> | Sparse {n ; v} ->
begin begin
Format.fprintf ppf "@[[ %d | " n; Format.fprintf ppf "@[[ %d | " n;
List.iter (fun (i,x) -> Format.fprintf ppf "@[(%d, %f); @]" i x) v; List.iter (fun {index ; value} -> Format.fprintf ppf "@[(%d, %f); @]" index value) v;
Format.fprintf ppf "]@]" Format.fprintf ppf "]@]"
end end
@ -225,18 +202,26 @@ let dot v v' =
let d_sp v' {n ; v} = let d_sp v' {n ; v} =
if n <> Vec.dim v' then if n <> Vec.dim v' then
invalid_arg "Inconsistent dimensions"; invalid_arg "Inconsistent dimensions";
List.fold_left (fun accu (i, v_i) -> accu +. v_i *. v'.{i}) 0. v List.fold_left (fun accu {index ; value} -> accu +. value *. v'.{index}) 0. v
in in
let sp_sp {n ; v} {n=n' ; v=v'} = let sp_sp {n ; v} {n=n' ; v=v'} =
if n <> n' then if n <> n' then
invalid_arg "Inconsistent dimensions"; invalid_arg "Inconsistent dimensions";
List.fold_left (fun accu (i, v_i) -> let rec aux accu = function
match List.assoc_opt i v' with | (({index=i ; value=v1} :: r1) as s1), (({index=j ; value=v2}::r2) as s2)->
| Some w_i -> accu +. v_i *. w_i begin
| None -> accu match compare i j with
) 0. v | -1 -> aux accu (r1, s2)
| 1 -> aux accu (s1, r2)
| 0 -> aux (accu +. v1 *. v2) (r1, r2)
| _ -> assert false
end
| ([], _ )
| (_ , []) -> accu
in
aux 0. (v, v')
in in
match v, v' with match v, v' with

View File

@ -60,7 +60,7 @@ val sub : ?threshold:float -> t -> t -> t
(** Subtract two vectors *) (** Subtract two vectors *)
val axpy : ?threshold:float -> ?alpha:float -> t -> t -> t val axpy : ?threshold:float -> ?alpha:float -> t -> t -> t
(** $a \mathfb{x} + \mathfb{y}$ *) (** {% $a \mathbf{x} + \mathbf{y}$ %} *)
val dot : t -> t -> float val dot : t -> t -> float
(** Dot product. *) (** Dot product. *)