10
1
mirror of https://gitlab.com/scemama/QCaml.git synced 2024-12-22 20:33:36 +01:00
QCaml/Utils/Davidson.ml

166 lines
3.8 KiB
OCaml
Raw Normal View History

2019-02-27 14:56:59 +01:00
open Lacaml.D
type t
let make
?guess
2019-02-28 16:08:28 +01:00
?(n_states=1)
2019-08-26 22:34:41 +02:00
?(n_iter=8)
?(threshold=1.e-7)
2019-02-27 14:56:59 +01:00
diagonal
2019-02-28 12:30:20 +01:00
matrix_prod
2019-02-27 14:56:59 +01:00
=
(* Size of the matrix to diagonalize *)
let n = Vec.dim diagonal in
2019-02-27 14:56:59 +01:00
2019-05-07 17:00:44 +02:00
let m = n_states in
2019-02-27 14:56:59 +01:00
2019-05-27 17:35:28 +02:00
(* Create guess vectors u, with unknown vectors initialized to unity. *)
2019-05-07 17:00:44 +02:00
let init_vectors m =
2019-04-02 11:54:04 +02:00
let init_vector k =
Vector.sparse_of_assoc_list n [ (k,1.0) ]
2019-02-27 14:56:59 +01:00
in
2019-04-02 11:54:04 +02:00
Array.init m (fun i -> init_vector (i+1))
|> Matrix.sparse_of_vector_array
|> Matrix.to_mat
2019-02-27 14:56:59 +01:00
in
2019-05-07 17:00:44 +02:00
let guess =
match guess with
| Some vectors -> vectors
| None -> init_vectors m
in
let guess =
if Mat.dim2 guess = n_states then guess
else
(Mat.to_col_vecs_list guess) @
(Mat.to_col_vecs_list (init_vectors (m-(Mat.dim2 guess))) )
|> Mat.of_col_vecs_list
in
2019-02-27 14:56:59 +01:00
let pick_new u =
Mat.to_col_vecs_list u
|> Util.list_pack m
|> List.rev
|> List.hd
in
2019-05-07 17:00:44 +02:00
let u_new = Mat.to_col_vecs_list guess in
2019-02-27 14:56:59 +01:00
2019-05-07 17:00:44 +02:00
let rec iteration u u_new w iter macro =
2019-02-27 14:56:59 +01:00
(* u is a list of orthonormal vectors, on which the operator has
been applied : w = op.u
2019-05-07 17:00:44 +02:00
u_new is a list of vectors which will increase the size of the
2019-02-27 14:56:59 +01:00
space.
*)
(* Orthonormalize input vectors u_new *)
let u_new_ortho =
List.concat [u ; u_new]
|> Mat.of_col_vecs_list
|> Util.qr_ortho
|> pick_new
in
2019-05-27 17:35:28 +02:00
(* Apply the operator to the m last vectors *)
2019-02-27 14:56:59 +01:00
let w_new =
2019-02-28 12:30:20 +01:00
matrix_prod (
u_new_ortho
|> Mat.of_col_vecs_list
2019-02-28 16:08:28 +01:00
|> Matrix.dense_of_mat )
2019-02-28 12:30:20 +01:00
|> Matrix.to_mat
|> Mat.to_col_vecs_list
2019-02-27 14:56:59 +01:00
in
(* Data for the next iteration *)
let u_next =
List.concat [ u ; u_new_ortho ]
and w_next =
List.concat [ w ; w_new ]
in
(* Build the small matrix h = <U_k | W_l> *)
let m_U =
Mat.of_col_vecs_list u_next
and m_W =
Mat.of_col_vecs_list w_next
in
let m_h =
gemm ~transa:`T m_U m_W
in
(* Diagonalize h *)
let y, lambda =
Util.diagonalize_symm m_h
in
(* Express m lowest eigenvectors of h in the large basis *)
let m_new_U =
gemm ~n:m m_U y
and m_new_W =
gemm ~n:m m_W y
in
(* Compute the residual as proposed new vectors *)
let u_proposed =
2019-04-03 18:09:13 +02:00
Mat.init_cols n m (fun i k ->
2019-08-26 22:34:41 +02:00
let delta = lambda.{k} -. diagonal.{i} in
2019-05-07 17:00:44 +02:00
let delta =
2019-08-26 22:34:41 +02:00
if abs_float delta > 1.e-2 then delta
else if delta > 0. then 1.e-2
else (-1.e-2)
2019-05-07 17:00:44 +02:00
in
2019-08-26 22:34:41 +02:00
(lambda.{k} *. m_new_U.{i,k} -. m_new_W.{i,k}) /. delta
)
2019-02-27 14:56:59 +01:00
|> Mat.to_col_vecs_list
in
2019-04-03 18:09:13 +02:00
2020-03-26 17:43:11 +01:00
let residual_norms = List.rev @@ List.rev_map nrm2 u_proposed in
2019-05-07 17:00:44 +02:00
let residual_norm =
List.fold_left (fun accu i -> accu +. i *. i) 0. residual_norms
|> sqrt
in
2019-02-27 14:56:59 +01:00
2019-04-03 18:09:13 +02:00
if Parallel.master then
2019-05-07 17:00:44 +02:00
begin
Printf.printf "%3d " iter;
Vec.iteri (fun i x -> if (i<=m) then Printf.printf "%16.10f " x) lambda;
Printf.printf "%16.8e%!\n" residual_norm
end;
2019-02-27 14:56:59 +01:00
2019-04-04 13:14:01 +02:00
(* Make new vectors sparse *)
2019-08-26 22:34:41 +02:00
2019-04-04 13:14:01 +02:00
let u_proposed =
Mat.of_col_vecs_list u_proposed
in
let maxu = lange u_proposed ~norm:`M in
2019-11-14 10:58:57 +01:00
let thr = maxu *. 0.00001 in
2019-04-04 13:14:01 +02:00
let u_proposed =
Mat.map (fun x -> if abs_float x < thr then 0. else x) u_proposed
|> Mat.to_col_vecs_list
in
2019-11-14 10:58:57 +01:00
(*
Format.printf "%a@." Matrix.pp_matrix @@ Matrix.dense_of_mat m_new_U;
*)
2019-04-04 13:14:01 +02:00
2019-05-07 17:00:44 +02:00
if residual_norm > threshold && macro > 0 then
let u_next, w_next, iter, macro =
2019-02-27 14:56:59 +01:00
if iter = n_iter then
m_new_U |> pick_new,
m_new_W |> pick_new,
2019-05-07 17:00:44 +02:00
0, (macro-1)
2019-02-27 14:56:59 +01:00
else
2019-05-07 17:00:44 +02:00
u_next, w_next, (iter+1), macro
2019-02-27 14:56:59 +01:00
in
2019-09-10 18:39:14 +02:00
(iteration [@tailcall]) u_next u_proposed w_next iter macro
2019-02-27 14:56:59 +01:00
else
(m_new_U |> pick_new |> Mat.of_col_vecs_list), lambda
2019-02-27 14:56:59 +01:00
in
2019-08-26 22:34:41 +02:00
iteration [] u_new [] 1 30
2019-02-27 14:56:59 +01:00