10
1
mirror of https://gitlab.com/scemama/QCaml.git synced 2024-11-18 12:03:40 +01:00
QCaml/CI/CI.ml

1007 lines
30 KiB
OCaml
Raw Normal View History

2019-02-22 00:18:32 +01:00
open Lacaml.D
2019-03-19 19:07:55 +01:00
open Util
2019-02-22 00:18:32 +01:00
2019-03-04 22:56:03 +01:00
module Ds = DeterminantSpace
2019-03-18 12:41:32 +01:00
module Sd = Spindeterminant
2019-02-22 00:18:32 +01:00
type t =
{
e_shift : float ; (* Diagonal energy shift for increasing numerical precision *)
2019-02-22 19:19:11 +01:00
det_space : Ds.t ;
2019-02-28 12:30:20 +01:00
m_H : Matrix.t lazy_t ;
m_S2 : Matrix.t lazy_t ;
2019-02-22 00:18:32 +01:00
eigensystem : (Mat.t * Vec.t) lazy_t;
2019-02-28 16:55:50 +01:00
n_states : int;
2019-02-22 00:18:32 +01:00
}
let det_space t = t.det_space
2019-02-28 16:55:50 +01:00
let n_states t = t.n_states
2019-02-26 11:58:53 +01:00
let m_H t = Lazy.force t.m_H
2019-02-22 00:18:32 +01:00
2019-02-26 11:58:53 +01:00
let m_S2 t = Lazy.force t.m_S2
2019-02-22 19:19:11 +01:00
2019-02-22 00:18:32 +01:00
let eigensystem t = Lazy.force t.eigensystem
2019-03-20 23:10:53 +01:00
let mo_class t = DeterminantSpace.mo_class t.det_space
2019-02-22 00:18:32 +01:00
let eigenvectors t =
let (x,_) = eigensystem t in x
let eigenvalues t =
let (_,x) = eigensystem t in x
2019-02-22 19:19:11 +01:00
let h_integrals mo_basis =
2019-02-22 00:18:32 +01:00
let one_e_ints = MOBasis.one_e_ints mo_basis
and two_e_ints = MOBasis.two_e_ints mo_basis
in
2019-02-22 19:19:11 +01:00
( (fun i j _ -> one_e_ints.{i,j}),
(fun i j k l s s' ->
if s' = Spin.other s then
ERI.get_phys two_e_ints i j k l
else
(ERI.get_phys two_e_ints i j k l) -.
(ERI.get_phys two_e_ints i j l k)
) )
let h_ij mo_basis ki kj =
let integrals =
List.map (fun f -> f mo_basis)
[ h_integrals ]
in
CIMatrixElement.make integrals ki kj
|> List.hd
2019-02-22 00:18:32 +01:00
2019-04-04 00:28:29 +02:00
let h_ij_non_zero mo_basis deg_a deg_b ki kj =
let integrals =
List.map (fun f -> f mo_basis)
[ h_integrals ]
in
CIMatrixElement.non_zero integrals deg_a deg_b ki kj
|> List.hd
2019-02-28 19:37:54 +01:00
let create_matrix_arbitrary f det_space =
lazy (
2019-03-04 22:56:03 +01:00
let ndet = Ds.size det_space in
let data =
2019-02-28 19:37:54 +01:00
match Ds.determinants det_space with
2019-03-04 22:56:03 +01:00
| Ds.Arbitrary a -> a
2019-02-28 19:37:54 +01:00
| _ -> assert false
in
2019-03-04 22:56:03 +01:00
let det_alfa = data.Ds.det_alfa
and det_beta = data.Ds.det_beta
and det = data.Ds.det
and index_start = data.Ds.index_start
in
2019-02-22 00:18:32 +01:00
2019-02-28 16:55:50 +01:00
2019-03-04 22:56:03 +01:00
(** Array of (list of singles, list of doubles) in the beta spin *)
let degree_bb =
Array.map (fun det_i ->
let deg = Spindeterminant.degree det_i in
let doubles =
Array.mapi (fun i det_j ->
let d = deg det_j in
if d < 3 then
Some (i,d,det_j)
else
None
) det_beta
|> Array.to_list
|> Util.list_some
in
let singles =
List.filter (fun (i,d,det_j) -> d < 2) doubles
|> List.map (fun (i,_,det_j) -> (i,det_j))
in
let doubles =
List.map (fun (i,_,det_j) -> (i,det_j)) doubles
in
(singles, doubles)
) det_beta
in
let task (i,i_dets) =
2019-03-18 23:38:01 +01:00
let shift = index_start.(i) in
2019-02-28 19:37:54 +01:00
2019-03-18 23:38:01 +01:00
let result =
Array.init (index_start.(i+1) - shift)
(fun _ -> [])
in
2019-03-04 22:56:03 +01:00
2019-03-18 23:38:01 +01:00
(** Update function when ki and kj are connected *)
2019-04-04 00:28:29 +02:00
let update i j deg_a deg_b ki kj =
let x = f deg_a deg_b ki kj in
2019-03-18 23:38:01 +01:00
if abs_float x > Constants.epsilon then
result.(i - shift) <- (j, x) :: result.(i - shift) ;
in
2019-03-04 22:56:03 +01:00
2019-03-18 23:38:01 +01:00
let i_alfa = det_alfa.(i) in
let deg_a = Spindeterminant.degree i_alfa in
2019-03-04 22:56:03 +01:00
2019-03-18 23:38:01 +01:00
Array.iteri (fun j j_dets ->
let j_alfa = det_alfa.(j) in
let degree_a = deg_a j_alfa in
2019-03-04 22:56:03 +01:00
2019-03-18 23:38:01 +01:00
begin
match degree_a with
| 2 ->
2019-03-04 22:56:03 +01:00
Array.iteri (fun i' i_b ->
try
Array.iteri (fun j' j_b ->
2019-03-18 23:38:01 +01:00
if j_b >= i_b then
( if j_b = i_b then
( let i_beta = det_beta.(i_b) in
let ki = Determinant.of_spindeterminants i_alfa i_beta in
let kj = Determinant.of_spindeterminants j_alfa i_beta in
update (index_start.(i) + i')
2019-04-04 00:28:29 +02:00
(index_start.(j) + j' + 1) 2 0 ki kj);
2019-03-18 23:38:01 +01:00
raise Exit)
2019-03-04 22:56:03 +01:00
) j_dets
2019-03-18 23:38:01 +01:00
with Exit -> ()
2019-03-04 22:56:03 +01:00
) i_dets
2019-03-18 23:38:01 +01:00
| 1 ->
2019-03-04 22:56:03 +01:00
Array.iteri (fun i' i_b ->
let i_beta = det_beta.(i_b) in
let ki = Determinant.of_spindeterminants i_alfa i_beta in
let singles, _ = degree_bb.(i_b) in
let rec aux singles j' =
match singles with
| [] -> ()
| (js, j_beta)::r_singles ->
begin
match compare js j_dets.(j') with
| -1 -> aux r_singles j'
| 0 ->
2019-03-18 23:38:01 +01:00
let kj =
Determinant.of_spindeterminants j_alfa j_beta
in (update
2019-03-04 22:56:03 +01:00
(index_start.(i) + i') (index_start.(j) + j' + 1)
2019-04-04 00:28:29 +02:00
1 (Determinant.degree_beta ki kj) ki kj;
2019-03-18 23:38:01 +01:00
aux r_singles (j'+1);)
2019-03-04 22:56:03 +01:00
| 1 -> if (j' < Array.length j_dets) then aux singles (j'+1)
| _ -> assert false
end
in aux singles 0
) i_dets
2019-03-18 23:38:01 +01:00
| 0 ->
2019-03-04 22:56:03 +01:00
Array.iteri (fun i' i_b ->
let i_beta = det_beta.(i_b) in
let ki = Determinant.of_spindeterminants i_alfa i_beta in
let _, doubles = degree_bb.(i_b) in
let rec aux doubles j' =
match doubles with
| [] -> ()
| (js, j_beta)::r_doubles ->
begin
match compare js j_dets.(j') with
| -1 -> aux r_doubles j'
| 0 ->
2019-03-18 23:38:01 +01:00
let kj =
Determinant.of_spindeterminants j_alfa j_beta
in (update
2019-03-04 22:56:03 +01:00
(index_start.(i) + i') (index_start.(j) + j' + 1)
2019-04-04 00:28:29 +02:00
0 (Determinant.degree_beta ki kj) ki kj;
2019-03-18 23:38:01 +01:00
aux r_doubles (j'+1);)
2019-03-04 22:56:03 +01:00
| 1 -> if (j' < Array.length j_dets) then aux doubles (j'+1)
| _ -> assert false
end
in aux doubles 0
) i_dets
2019-03-18 23:38:01 +01:00
| _ -> ();
end
) det;
let r =
Array.map (fun l ->
List.rev l
|> Vector.sparse_of_assoc_list ndet
) result
in (i,r)
in
2019-03-04 22:56:03 +01:00
let result =
if Parallel.master then
Array.init ndet (fun _ -> Vector.sparse_of_assoc_list ndet [])
else
Array.init ndet (fun _ -> Vector.sparse_of_assoc_list ndet [])
in
let n_det_alfa =
Array.length det_alfa
in
Array.mapi (fun i i_dets -> i, i_dets) det
|> Array.to_list
|> Stream.of_list
|> Farm.run ~ordered:false ~f:task
|> Stream.iter (fun (k, r) ->
let shift = index_start.(k) in
let det_k = det.(k) in
Array.iteri (fun j r_j -> result.(shift+det_k.(j)) <- r_j) r;
Printf.eprintf "%8d / %8d\r%!" (k+1) n_det_alfa;
) ;
Matrix.sparse_of_vector_array result
2019-02-28 19:37:54 +01:00
)
(* Create a matrix using the fact that the determinant space is made of
the outer product of spindeterminants. *)
let create_matrix_spin f det_space =
lazy (
let ndet = Ds.size det_space in
let a, b =
match Ds.determinants det_space with
| Ds.Spin (a,b) -> (a,b)
| _ -> assert false
in
let n_beta = Array.length b in
2019-02-28 16:55:50 +01:00
2019-02-28 19:37:54 +01:00
(** Array of (list of singles, list of doubles) in the beta spin *)
let degree_bb =
Array.map (fun det_i ->
2019-02-28 18:18:26 +01:00
let deg = Spindeterminant.degree det_i in
let doubles =
Array.mapi (fun i det_j ->
2019-02-28 19:37:54 +01:00
let d = deg det_j in
if d < 3 then
Some (i,d,det_j)
else
None
2019-02-28 18:18:26 +01:00
) b
|> Array.to_list
|> Util.list_some
in
let singles =
List.filter (fun (i,d,det_j) -> d < 2) doubles
|> List.map (fun (i,_,det_j) -> (i,det_j))
in
let doubles =
List.map (fun (i,_,det_j) -> (i,det_j)) doubles
in
(singles, doubles)
) b
2019-02-28 19:37:54 +01:00
in
let a = Array.to_list a
and b = Array.to_list b
in
2019-03-02 18:50:12 +01:00
let task (i,i_alfa) =
2019-03-18 23:38:01 +01:00
let result =
Array.init n_beta (fun _ -> [])
in
2019-03-04 22:56:03 +01:00
2019-03-18 23:38:01 +01:00
(** Update function when ki and kj are connected *)
2019-04-04 00:28:29 +02:00
let update i j deg_a deg_b ki kj =
let x = f deg_a deg_b ki kj in
2019-03-18 23:38:01 +01:00
if abs_float x > Constants.epsilon then
result.(i) <- (j, x) :: result.(i) ;
in
2019-03-04 22:56:03 +01:00
2019-03-18 23:38:01 +01:00
let j = ref 1 in
let deg_a = Spindeterminant.degree i_alfa in
List.iter (fun j_alfa ->
let degree_a = deg_a j_alfa in
begin
match degree_a with
| 2 ->
let i' = ref 0 in
List.iteri (fun ib i_beta ->
let ki = Determinant.of_spindeterminants i_alfa i_beta in
let kj = Determinant.of_spindeterminants j_alfa i_beta in
2019-04-04 00:28:29 +02:00
update !i' (ib + !j) 2 0 ki kj;
2019-03-18 23:38:01 +01:00
incr i';
) b;
| 1 ->
let i' = ref 0 in
List.iteri (fun ib i_beta ->
let ki = Determinant.of_spindeterminants i_alfa i_beta in
let singles, _ = degree_bb.(ib) in
List.iter (fun (j', j_beta) ->
let kj = Determinant.of_spindeterminants j_alfa j_beta in
2019-04-04 00:28:29 +02:00
update !i' (j' + !j) 1 (Determinant.degree_beta ki kj) ki kj
2019-03-18 23:38:01 +01:00
) singles;
incr i';
) b;
| 0 ->
let i' = ref 0 in
List.iteri (fun ib i_beta ->
let ki = Determinant.of_spindeterminants i_alfa i_beta in
let _singles, doubles = degree_bb.(ib) in
List.iter (fun (j', j_beta) ->
let kj = Determinant.of_spindeterminants j_alfa j_beta in
2019-04-04 00:28:29 +02:00
update !i' (j' + !j) 0 (Determinant.degree_beta ki kj) ki kj
2019-03-18 23:38:01 +01:00
) doubles;
incr i';
) b;
| _ -> ();
end;
j := !j + n_beta
) a;
let r =
Array.map (fun l ->
List.rev l
|> Vector.sparse_of_assoc_list ndet
) result
in (i,r)
2019-03-02 17:05:15 +01:00
in
2019-03-02 18:50:12 +01:00
2019-03-02 17:05:15 +01:00
let result =
2019-04-02 11:54:04 +02:00
Array.init ndet (fun _ -> Vector.sparse_of_assoc_list ndet [])
2019-03-02 17:05:15 +01:00
in
2019-03-02 18:50:12 +01:00
List.mapi (fun i i_alfa -> i*n_beta, i_alfa) a
|> Stream.of_list
|> Farm.run ~ordered:false ~f:task
|> Stream.iter (fun (k, r) ->
Array.iteri (fun j r_j -> result.(k+j) <- r_j) r;
2019-04-02 11:54:04 +02:00
Printf.eprintf "%8d / %8d\r%!" (k+Array.length r) ndet;
2019-03-02 18:50:12 +01:00
) ;
2019-03-02 17:05:15 +01:00
Matrix.sparse_of_vector_array result
2019-02-28 19:37:54 +01:00
)
2019-04-03 18:09:13 +02:00
(* Create a matrix using the fact that the determinant space is made of
the outer product of spindeterminants. *)
let create_matrix_spin_computed f det_space =
lazy (
let ndet = Ds.size det_space in
let a, b =
match Ds.determinants det_space with
| Ds.Spin (a,b) -> (a,b)
| _ -> assert false
in
let n_beta = Array.length b in
2019-04-03 22:17:20 +02:00
let h i_alfa j_alfa =
2019-04-04 00:28:29 +02:00
let deg_a = Spindeterminant.degree a.(i_alfa) a.(j_alfa) in
match deg_a with
2019-04-03 22:17:20 +02:00
| 2 ->
let ai, aj = a.(i_alfa), a.(j_alfa) in
(fun i_beta j_beta ->
if i_beta <> j_beta then 0. else
2019-04-04 00:28:29 +02:00
let deg_b = 0 in
let ki = Determinant.of_spindeterminants ai b.(i_beta) in
let kj = Determinant.of_spindeterminants aj b.(j_beta) in
f deg_a deg_b ki kj
2019-04-03 22:17:20 +02:00
)
| 1 ->
let ai, aj = a.(i_alfa), a.(j_alfa) in
(fun i_beta j_beta ->
2019-04-04 00:28:29 +02:00
let deg_b = Spindeterminant.degree b.(i_beta) b.(j_beta) in
match deg_b with
2019-04-03 22:17:20 +02:00
| 0 | 1 ->
let ki = Determinant.of_spindeterminants ai b.(i_beta) in
let kj = Determinant.of_spindeterminants aj b.(j_beta) in
2019-04-04 00:28:29 +02:00
f deg_a deg_b ki kj
2019-04-03 22:17:20 +02:00
| _ -> 0.
)
| 0 ->
let ai, aj = a.(i_alfa), a.(j_alfa) in
(fun i_beta j_beta ->
2019-04-04 00:28:29 +02:00
let deg_b = Spindeterminant.degree b.(i_beta) b.(j_beta) in
match deg_b with
| 0 | 1 | 2 ->
let deg_b = Spindeterminant.degree b.(i_beta) b.(j_beta) in
let ki = Determinant.of_spindeterminants ai b.(i_beta) in
let kj = Determinant.of_spindeterminants aj b.(j_beta) in
f deg_a deg_b ki kj
| _ -> 0.
2019-04-03 18:09:13 +02:00
)
| _ -> (fun _ _ -> 0.)
in
let i_prev = ref (-10)
and result = ref (fun _ -> 0.)
in
2019-04-05 09:46:23 +02:00
let h123 = ref (fun _ -> 0.) in
2019-04-03 18:09:13 +02:00
let g i =
2019-05-27 17:35:28 +02:00
(*
i : index of the i-th determinant. 1 <= i <= ndet
i_prev : index of the i-th determinant in the previous function call.
1 <= i_prev <= ndet
i_a : index of the i_a-th alpha determinant. 0 <= i_a < n_alfa
i_b : index of the i_b-th beta determinant. 0 <= i_b < n_beta
j0 : index - 1 of the first determinant with the same alfa component
0 <= j0 < n_beta*(n_alfa-1)
j1 : index - 1 of the next determinant with the 1st beta determinant
n_beta <= j1 <= ndet
j_a : index of the j_a-th alpha determinant. 0 <= j_a < n_alfa
j_b : index of the j_b-th beta determinant. 0 <= j_b < n_beta
*)
2019-04-03 18:09:13 +02:00
if i <> !i_prev then
begin
i_prev := i;
let i_a = (i-1)/n_beta in
2019-04-05 09:46:23 +02:00
let h1 = h i_a in
let i_b = i - i_a*n_beta - 1 in
2019-05-27 17:35:28 +02:00
let j0 = ref (2*ndet) in
let j1 = ref (2*ndet) in
2019-04-05 09:46:23 +02:00
let j_a = ref 0 in
2019-04-04 00:28:29 +02:00
result := fun j ->
2019-05-27 17:35:28 +02:00
if (!j0 < j) && (j <= !j1) then
()
2019-04-05 09:46:23 +02:00
else
begin
2019-05-27 17:35:28 +02:00
if (!j1 < j) && (j <= !j1 + n_beta) then
2019-04-05 09:46:23 +02:00
begin
incr j_a;
j0 := !j1;
end
else
begin
j_a := (j-1)/n_beta;
j0 := !j_a * n_beta;
end;
j1 := !j0 + n_beta;
h123 := h1 !j_a i_b;
2019-05-27 17:35:28 +02:00
end;
let j_b = j - !j0 - 1 in
!h123 j_b
2019-04-03 18:09:13 +02:00
end;
!result
in
Matrix.of_fun ndet ndet g
)
2019-03-18 12:41:32 +01:00
2019-04-02 11:54:04 +02:00
let make ?(n_states=1) ?(algo=`Direct) det_space =
2019-02-28 16:55:50 +01:00
let mo_basis = Ds.mo_basis det_space in
2019-03-02 18:50:12 +01:00
let e_shift =
let d0 =
Ds.determinant_stream det_space
|> Stream.next
in
2019-04-04 00:28:29 +02:00
h_ij_non_zero mo_basis 0 0 d0 d0
in
let m_H =
2019-03-02 18:50:12 +01:00
(* While in a sequential region, initiate the parallel
2019-03-04 22:56:03 +01:00
4-idx transformation to avoid nested parallel jobs
2019-03-18 23:38:01 +01:00
*)
2019-03-02 18:50:12 +01:00
ignore @@ MOBasis.two_e_ints mo_basis;
2019-02-28 19:37:54 +01:00
let f =
match Ds.determinants det_space with
| Ds.Arbitrary _ -> create_matrix_arbitrary
2019-04-03 18:09:13 +02:00
| Ds.Spin _ ->
if algo = `Direct then
create_matrix_spin_computed
else
create_matrix_spin
2019-02-28 19:37:54 +01:00
in
2019-04-04 00:28:29 +02:00
f (fun deg_a deg_b ki kj ->
if deg_a + deg_b > 0 then
h_ij_non_zero mo_basis deg_a deg_b ki kj
else
2019-04-04 00:28:29 +02:00
h_ij_non_zero mo_basis 0 0 ki ki -. e_shift
) det_space
2019-02-28 12:50:42 +01:00
in
2019-02-28 16:55:50 +01:00
2019-02-28 19:37:54 +01:00
let m_S2 =
let f =
match Ds.determinants det_space with
| Ds.Arbitrary _ -> create_matrix_arbitrary
| Ds.Spin _ -> create_matrix_spin
in
2019-04-04 00:28:29 +02:00
f (fun _deg_a _deg_b ki kj -> CIMatrixElement.make_s2 ki kj) det_space
(*TODO*)
2019-02-22 00:18:32 +01:00
in
2019-02-28 16:55:50 +01:00
2019-02-22 00:18:32 +01:00
let eigensystem = lazy (
2019-04-02 11:54:04 +02:00
let eigensystem_incore () =
let m_H =
Lazy.force m_H
in
let diagonal =
2019-04-02 13:54:16 +02:00
Parallel.broadcast (lazy (
Vec.init (Matrix.dim1 m_H) (fun i -> Matrix.get m_H i i)
))
2019-04-02 11:54:04 +02:00
in
let matrix_prod psi =
2019-04-02 13:54:16 +02:00
let result =
2019-04-03 18:09:13 +02:00
Matrix.mm ~transa:`T m_H psi
2019-04-02 13:54:16 +02:00
in
Parallel.broadcast (lazy result)
2019-04-02 11:54:04 +02:00
in
let eigenvectors, eigenvalues =
2019-04-02 13:54:16 +02:00
let result =
2019-04-02 11:54:04 +02:00
Davidson.make ~threshold:1.e-6 ~n_states diagonal matrix_prod
2019-04-02 13:54:16 +02:00
in
Parallel.broadcast (lazy result)
2019-04-02 11:54:04 +02:00
in
let eigenvalues = Vec.map (fun x -> x +. e_shift) eigenvalues in
eigenvectors, eigenvalues
2019-02-28 12:30:20 +01:00
in
2019-04-02 11:54:04 +02:00
let eigensystem_direct () =
2019-04-03 18:09:13 +02:00
let m_H =
Lazy.force m_H
in
2019-04-02 11:54:04 +02:00
let diagonal =
2019-04-03 18:09:13 +02:00
Parallel.broadcast (lazy (
Vec.init (Matrix.dim1 m_H) (fun i -> Matrix.get m_H i i)
))
2019-04-02 11:54:04 +02:00
in
let matrix_prod psi =
2019-04-03 18:09:13 +02:00
let result =
2019-04-03 22:17:20 +02:00
Matrix.parallel_mm ~transa:`T ~transb:`N psi m_H
2019-04-03 18:09:13 +02:00
|> Matrix.transpose
in
Parallel.broadcast (lazy result)
2019-04-02 11:54:04 +02:00
in
let eigenvectors, eigenvalues =
let result =
Davidson.make ~threshold:1.e-6 ~n_states diagonal matrix_prod
in
Parallel.broadcast (lazy result)
in
let eigenvalues = Vec.map (fun x -> x +. e_shift) eigenvalues in
eigenvectors, eigenvalues
in
2019-04-02 11:54:04 +02:00
match algo with
| `Direct -> eigensystem_direct ()
| `InCore -> eigensystem_incore ()
2019-02-22 00:18:32 +01:00
)
in
{ det_space ; e_shift ; m_H ; m_S2 ; eigensystem ; n_states }
2019-02-22 00:18:32 +01:00
2019-02-27 14:56:59 +01:00
2019-03-18 12:41:32 +01:00
2019-03-18 23:38:01 +01:00
let second_order_sum { det_space ; m_H ; m_S2 ; eigensystem ; n_states }
2019-03-23 14:54:59 +01:00
list_holes list_particles ?(unique=true) is_internal
2019-03-21 21:48:21 +01:00
i_o1_alfa alfa_o2_i w_alfa psi0 =
2019-03-23 14:54:59 +01:00
let list_holes = Array.of_list list_holes in
let list_particles = Array.of_list list_particles in
2019-03-18 12:41:32 +01:00
let psi0 =
let stream =
Ds.determinant_stream det_space
in
Array.init (Ds.size det_space) (fun i ->
2019-03-21 21:48:21 +01:00
(Stream.next stream), (Mat.copy_row psi0 (i+1)) )
2019-03-18 12:41:32 +01:00
in
2019-03-19 19:07:55 +01:00
2019-03-18 19:17:15 +01:00
2019-03-18 23:38:01 +01:00
2019-03-19 00:10:34 +01:00
let symmetric = i_o1_alfa == alfa_o2_i in
2019-03-18 12:41:32 +01:00
let det_contribution i =
2019-03-18 19:17:15 +01:00
2019-03-22 00:34:00 +01:00
let already_generated =
if unique then
(fun alfa ->
if is_internal alfa then
true
else
let rec aux = function
| -1 -> false
| j -> Determinant.degree (fst psi0.(j)) alfa <= 2
|| aux (j-1)
in
aux (i-1)
2019-03-23 01:06:38 +01:00
)
else
2019-03-22 00:34:00 +01:00
is_internal
2019-03-19 00:10:34 +01:00
in
2019-03-18 19:17:15 +01:00
let psi_filtered_idx =
let rec aux accu = function
2019-03-18 23:38:01 +01:00
| j when j < i -> List.rev accu
2019-03-19 19:07:55 +01:00
| j ->
if Determinant.degree (fst psi0.(i)) (fst psi0.(j)) < 4
then
2019-03-18 23:38:01 +01:00
aux (j::accu) (j-1)
else
aux accu (j-1)
2019-03-18 19:17:15 +01:00
in aux [] (Array.length psi0 - 1)
in
let psi_filtered =
List.map (fun i -> psi0.(i)) psi_filtered_idx
2019-03-18 12:41:32 +01:00
in
2019-03-18 19:17:15 +01:00
2019-03-18 23:38:01 +01:00
let psi_h_alfa alfa =
2019-03-18 19:17:15 +01:00
List.fold_left (fun accu (det, coef) ->
2019-03-21 21:48:21 +01:00
(* Single state here *)
accu +. coef.{1} *. (i_o1_alfa det alfa)) 0. psi_filtered
2019-03-18 23:38:01 +01:00
in
2019-03-23 14:54:59 +01:00
let alfa_h_psi alfa =
List.fold_left (fun accu (det, coef) ->
2019-03-21 21:48:21 +01:00
(* Single state here *)
accu +. coef.{1} *. (alfa_o2_i alfa det)) 0. psi_filtered
2019-03-18 23:38:01 +01:00
in
let psi_h_alfa_alfa_h_psi alfa =
if symmetric then
let x = psi_h_alfa alfa in x *. x
else
(psi_h_alfa alfa) *. (alfa_h_psi alfa)
2019-03-18 19:17:15 +01:00
in
let det_i = fst psi0.(i) in
2019-03-18 23:38:01 +01:00
let w_alfa = w_alfa det_i in
2019-03-18 19:17:15 +01:00
2019-03-18 23:38:01 +01:00
let same_spin =
List.fold_left (fun accu spin ->
accu +.
2019-03-19 19:07:55 +01:00
Array.fold_left (fun accu particle ->
2019-03-18 19:17:15 +01:00
accu +.
2019-03-19 19:07:55 +01:00
Array.fold_left (fun accu hole ->
2019-03-18 23:38:01 +01:00
if hole = particle then accu else
let alfa =
Determinant.single_excitation spin hole particle det_i
2019-03-18 19:17:15 +01:00
in
2019-03-18 23:38:01 +01:00
if Determinant.is_none alfa then accu else
let single =
if already_generated alfa then 0. else
w_alfa alfa *. psi_h_alfa_alfa_h_psi alfa
in
let double =
2019-03-19 19:07:55 +01:00
Array.fold_left (fun accu particle' ->
2019-03-23 01:06:38 +01:00
if particle' >= particle || particle' = hole then
2019-03-18 23:38:01 +01:00
accu
else
2019-03-18 19:17:15 +01:00
accu +.
2019-03-19 19:07:55 +01:00
Array.fold_left (fun accu hole' ->
2019-03-23 01:06:38 +01:00
if hole' = particle' || hole' = particle || hole' <= hole then
2019-03-18 19:17:15 +01:00
accu
else
2019-03-18 23:38:01 +01:00
let alfa =
2019-03-23 01:06:38 +01:00
Determinant.single_excitation
spin hole' particle' alfa
2019-03-18 23:38:01 +01:00
in
if Determinant.is_none alfa ||
already_generated alfa then
accu
else
accu +. w_alfa alfa *. psi_h_alfa_alfa_h_psi alfa
2019-03-23 14:54:59 +01:00
) 0. list_holes
) 0. list_particles
2019-03-18 23:38:01 +01:00
in
accu +. single +. double
2019-03-23 14:54:59 +01:00
) 0. list_holes
) 0. list_particles
2019-03-18 23:38:01 +01:00
) 0. [ Spin.Alfa ; Spin.Beta ]
in
let opposite_spin =
2019-03-19 19:07:55 +01:00
Array.fold_left (fun accu particle ->
2019-03-18 23:38:01 +01:00
accu +.
2019-03-19 19:07:55 +01:00
Array.fold_left (fun accu hole ->
2019-03-18 23:38:01 +01:00
if hole = particle then accu else
let alfa =
Determinant.single_excitation Spin.Alfa hole particle det_i
in
if Determinant.is_none alfa then accu else
2019-03-23 01:06:38 +01:00
let double_ab =
2019-03-19 19:07:55 +01:00
Array.fold_left (fun accu particle' ->
2019-03-23 01:06:38 +01:00
accu +.
Array.fold_left (fun accu hole' ->
if hole' = particle' then accu else
let alfa =
Determinant.double_excitation
Spin.Alfa hole particle
Spin.Beta hole' particle' det_i
in
if Determinant.is_none alfa ||
already_generated alfa then
accu
else
accu +. w_alfa alfa *. psi_h_alfa_alfa_h_psi alfa
2019-03-23 14:54:59 +01:00
) 0. list_holes
) 0. list_particles
2019-03-18 23:38:01 +01:00
in
2019-03-23 01:06:38 +01:00
accu +. double_ab
2019-03-23 14:54:59 +01:00
) 0. list_holes
) 0. list_particles
2019-03-18 19:17:15 +01:00
in
2019-03-18 23:38:01 +01:00
same_spin +. opposite_spin
in
2019-03-18 12:41:32 +01:00
2019-03-21 00:44:10 +01:00
Util.stream_range 0 (Array.length psi0 - 1)
|> Farm.run ~ordered:true ~f:det_contribution
|> Util.stream_to_list
2019-03-18 23:38:01 +01:00
2019-03-22 18:16:41 +01:00
2019-03-23 14:54:59 +01:00
let second_order_sum2 { det_space ; m_H ; m_S2 ; eigensystem ; n_states }
list_holes list_particles i_o1_alfa e0 psi0 =
let psi0 =
let stream =
Ds.determinant_stream det_space
in
Array.init (Ds.size det_space) (fun i ->
(Stream.next stream), (Mat.copy_row psi0 (i+1)) )
in
let determinants =
Ds.determinants_array det_space
|> Array.to_list
|> List.map (fun det_i ->
[ Spin.Alfa ; Spin.Beta ]
|> List.map (fun spin ->
List.map (fun particle ->
List.map (fun hole ->
[ [ Determinant.single_excitation spin hole particle det_i ] ;
List.map (fun particle' ->
List.map (fun hole' ->
Determinant.double_excitation
spin hole particle
spin hole' particle' det_i
) list_holes
) list_particles
|> List.concat
;
List.map (fun particle' ->
List.map (fun hole' ->
Determinant.double_excitation
spin hole particle
(Spin.other spin) hole' particle' det_i
) list_holes
) list_particles
|> List.concat
]
|> List.concat
) list_holes
) list_particles
|> List.concat
)
|> List.concat
)
|> List.concat
|> List.concat
|> List.filter (fun alfa -> not (Determinant.is_none alfa))
|> List.sort_uniq compare
in
List.fold_left (fun accu alfa ->
let alfa_o2 = i_o1_alfa alfa in
let a_h_psi =
Array.fold_left (fun accu (det,ci) -> ci.{1} *. (alfa_o2 det)) 0. psi0
in
accu +. (a_h_psi *. a_h_psi) /. (e0 -. (alfa_o2 alfa))
) 0. determinants
2019-03-22 18:16:41 +01:00
let is_internal det_space =
2019-03-26 01:20:17 +01:00
let mo_class = DeterminantSpace.mo_class det_space in
let numbits = Array.length @@ MOClass.mo_class_array mo_class in
2019-03-22 18:16:41 +01:00
let m l =
List.fold_left (fun accu i ->
2019-03-26 01:20:17 +01:00
let j = i-1 in
Bitstring.logor accu (Bitstring.shift_left_one numbits j)
) (Bitstring.zero numbits) l
2019-03-22 18:16:41 +01:00
in
let active_mask = m (MOClass.active_mos mo_class) in
let occ_mask = m (MOClass.core_mos mo_class) in
let inactive_mask = m (MOClass.inactive_mos mo_class) in
2019-03-25 19:28:38 +01:00
let occ_mask = Bitstring.logor occ_mask inactive_mask in
let neg_active_mask = Bitstring.lognot active_mask in
2019-03-22 18:16:41 +01:00
fun a ->
let alfa =
Determinant.alfa a
|> Spindeterminant.bitstring
in
2019-03-25 19:28:38 +01:00
if Bitstring.logand neg_active_mask alfa <> occ_mask then
2019-03-22 18:16:41 +01:00
false
else
let beta =
Determinant.beta a
|> Spindeterminant.bitstring
in
2019-03-25 19:28:38 +01:00
Bitstring.logand neg_active_mask beta = occ_mask
2019-03-22 18:16:41 +01:00
2019-03-23 14:54:59 +01:00
let _pt2_en ci =
2019-03-18 23:38:01 +01:00
let mo_basis = Ds.mo_basis ci.det_space in
2019-03-21 16:44:24 +01:00
let psi0, e0 = Parallel.broadcast ci.eigensystem in
2019-03-18 23:38:01 +01:00
let i_o1_alfa = h_ij mo_basis in
2019-03-19 19:07:55 +01:00
let w_alfa det_i =
let one_e, two_e = h_integrals mo_basis in
let fock_diag_alfa, fock_diag_beta =
Ds.fock_diag ci.det_space det_i
in
let h_aa alfa =
match Excitation.of_det det_i alfa with
| Excitation.Double (_,
{hole = h ; particle = p ; spin = s },
{hole = h'; particle = p'; spin = s'}) ->
let fock_diag1 =
if s = Spin.Alfa then
fock_diag_alfa
else
fock_diag_beta
in
let fock_diag2 =
if s' = Spin.Alfa then
fock_diag_alfa
else
fock_diag_beta
in
fock_diag1.(0) -. fock_diag1.(h)
+. (fock_diag1.(p ) -. two_e h p h p s s)
-. (fock_diag2.(h') -. two_e h h' h h' s s' +. two_e p h' p h' s s')
+. (fock_diag2.(p') -. two_e h p' h p' s s'
+. two_e p p' p p' s s' -. two_e h' p' h' p' s' s')
| Excitation.Single (_,
{hole = h ; particle = p ; spin = s }) ->
let fock_diag =
if s = Spin.Alfa then
fock_diag_alfa
else
fock_diag_beta
in
fock_diag.(0) -. fock_diag.(h)
+. (fock_diag.(p) -. two_e h p h p s s)
|> ignore;
h_ij mo_basis alfa alfa
2019-03-22 18:16:41 +01:00
| _ -> e0.{1} -. 1.0
2019-03-19 19:07:55 +01:00
in
2019-03-18 23:38:01 +01:00
let e0 = e0.{1} in
fun alfa ->
1. /. (e0 -. h_aa alfa)
in
2019-03-20 23:10:53 +01:00
let mo_class = mo_class ci in
let list_holes = List.concat
[ MOClass.inactive_mos mo_class ; MOClass.active_mos mo_class ]
and list_particles = List.concat
[ MOClass.active_mos mo_class ; MOClass.virtual_mos mo_class ]
in
2019-03-23 14:54:59 +01:00
second_order_sum ci list_holes list_particles
2019-03-22 18:16:41 +01:00
(is_internal ci.det_space) i_o1_alfa i_o1_alfa w_alfa psi0
2019-03-21 00:44:10 +01:00
|> List.fold_left (+.) 0.
2019-03-20 23:10:53 +01:00
2019-03-18 23:38:01 +01:00
2019-03-23 14:54:59 +01:00
let pt2_en ci =
let mo_basis = Ds.mo_basis ci.det_space in
let psi0, e0 = Parallel.broadcast ci.eigensystem in
let i_o1_alfa = h_ij mo_basis in
let mo_class = mo_class ci in
let list_holes = List.concat
[ MOClass.inactive_mos mo_class ; MOClass.active_mos mo_class ]
and list_particles = List.concat
[ MOClass.active_mos mo_class ; MOClass.virtual_mos mo_class ]
in
second_order_sum2 ci list_holes list_particles i_o1_alfa e0.{1} psi0
2019-03-18 23:38:01 +01:00
2019-03-22 18:16:41 +01:00
2019-03-18 23:38:01 +01:00
let pt2_mp ci =
let mo_basis = Ds.mo_basis ci.det_space in
let i_o1_alfa = h_ij mo_basis in
let eps = MOBasis.mo_energies mo_basis in
let w_alfa det_i alfa=
match Excitation.of_det det_i alfa with
| Excitation.Single (_, { hole ; particle ; spin })->
1./.(eps.{hole} -. eps.{particle})
| Excitation.Double (_, { hole=h ; particle=p ; spin=s },
{ hole=h'; particle=p'; spin=s'})->
1./.(eps.{h} +. eps.{h'} -. eps.{p} -. eps.{p'})
| _ -> assert false
in
2019-03-20 23:10:53 +01:00
let mo_class = mo_class ci in
let list_holes = List.concat
[ MOClass.inactive_mos mo_class ; MOClass.active_mos mo_class ]
and list_particles = List.concat
[ MOClass.active_mos mo_class ; MOClass.virtual_mos mo_class ]
in
2019-03-21 16:44:24 +01:00
let psi0, _ = Parallel.broadcast ci.eigensystem in
2019-03-23 14:54:59 +01:00
second_order_sum ci list_holes list_particles
2019-03-22 18:16:41 +01:00
(is_internal ci.det_space) i_o1_alfa i_o1_alfa w_alfa psi0
2019-03-21 00:44:10 +01:00
|> List.fold_left (+.) 0.
2019-03-18 23:38:01 +01:00
2019-03-19 00:10:34 +01:00
let variance ci =
let mo_basis = Ds.mo_basis ci.det_space in
2019-03-22 18:16:41 +01:00
let psi0, _ = Parallel.broadcast ci.eigensystem in
2019-03-19 00:10:34 +01:00
let i_o1_alfa = h_ij mo_basis in
let w_alfa _ _ = 1. in
2019-03-20 23:10:53 +01:00
let mo_class = mo_class ci in
let list_holes = List.concat
[ MOClass.inactive_mos mo_class ; MOClass.active_mos mo_class ]
and list_particles = List.concat
[ MOClass.active_mos mo_class ; MOClass.virtual_mos mo_class ]
in
2019-03-23 14:54:59 +01:00
second_order_sum ci list_holes list_particles
2019-03-22 18:16:41 +01:00
(is_internal ci.det_space) i_o1_alfa i_o1_alfa w_alfa psi0
2019-03-21 00:44:10 +01:00
|> List.fold_left (+.) 0.
2019-03-19 00:10:34 +01:00
2019-03-22 18:16:41 +01:00
let pt2_en_reference ci =
let mo_basis = Ds.mo_basis ci.det_space in
let psi0, e0 = Parallel.broadcast ci.eigensystem in
let aux_basis = mo_basis in
let ds =
2019-03-23 15:54:46 +01:00
DeterminantSpace.fci_of_mo_basis ~frozen_core:false aux_basis
2019-03-22 18:16:41 +01:00
in
let out_dets =
ds
|> DeterminantSpace.determinants_array
|> Array.to_list
|> List.filter (fun i -> not (is_internal ci.det_space i))
|> Array.of_list
in
let in_dets =
DeterminantSpace.determinants_array ci.det_space
in
let m_H_aux =
let h_aa =
Array.map (fun ki -> h_ij aux_basis ki ki) out_dets
in
Array.map (fun ki ->
Array.mapi (fun j kj ->
2019-03-23 01:06:38 +01:00
(h_ij aux_basis ki kj) /. (e0.{1} -. h_aa.(j))
2019-03-22 18:16:41 +01:00
) out_dets
) in_dets
|> Mat.of_array
in
let m_F_aux =
Array.map (fun ki ->
Array.map (fun kj ->
h_ij aux_basis ki kj
) out_dets
) in_dets
|> Mat.of_array
in
let m_HF =
gemm m_H_aux m_F_aux ~transb:`T
in
(gemm ~transa:`T psi0 @@ gemm m_HF psi0).{1,1}