QCaml/CI/CI.ml

160 lines
4.5 KiB
OCaml

open Lacaml.D
module De = Determinant
module Ex = Excitation
module Sp = Spindeterminant
type t =
{
det_space : Determinant_space.t ;
h_matrix : Mat.t lazy_t ;
eigensystem : (Mat.t * Vec.t) lazy_t;
}
let det_space t = t.det_space
let h_matrix t = Lazy.force t.h_matrix
let eigensystem t = Lazy.force t.eigensystem
let eigenvectors t =
let (x,_) = eigensystem t in x
let eigenvalues t =
let (_,x) = eigensystem t in x
let h_ij mo_basis ki kj =
let one_e_ints = MOBasis.one_e_ints mo_basis
and two_e_ints = MOBasis.two_e_ints mo_basis
and degree_a, degree_b = De.degrees ki kj
in
if degree_a+degree_b > 2 then
0.
else
begin
let kia = De.alfa ki and kib = De.beta ki
and kja = De.alfa kj and kjb = De.beta kj
in
let integral =
ERI.get_phys two_e_ints
in
let anti_integral i j k l =
integral i j k l -. integral i j l k
in
let single h p same opposite =
let same_spin =
Sp.to_list same
|> List.fold_left (fun accu i -> accu +. anti_integral h i p i) 0.
and opposite_spin =
Sp.to_list opposite
|> List.fold_left (fun accu i -> accu +. integral h i p i) 0.
and one_e =
one_e_ints.{h,p}
in one_e +. same_spin +. opposite_spin
in
let diag_element () =
let mo_a = Sp.to_list kia
and mo_b = Sp.to_list kib
in
let one_e =
List.concat [mo_a ; mo_b]
|> List.fold_left (fun accu i -> accu +. one_e_ints.{i,i}) 0.
and two_e =
let two_index i j = integral i j i j
and anti_two_index i j = anti_integral i j i j
in
let rec aux_same accu = function
| [] -> accu
| i :: rest ->
let new_accu =
List.fold_left (fun accu j -> accu +. anti_two_index i j) accu rest
in
aux_same new_accu rest
in
let rec aux_opposite accu other = function
| [] -> accu
| i :: rest ->
let new_accu =
List.fold_left (fun accu j -> accu +. two_index i j) accu other
in
aux_opposite new_accu other rest
in
(aux_same 0. mo_a) +. (aux_same 0. mo_b) +. (aux_opposite 0. mo_a mo_b)
in
one_e +. two_e
in
match degree_a, degree_b with
| 1, 1 -> (* alpha-beta double *)
begin
let ha, pa, phase_a = Ex.single_of_spindet kia kja in
let hb, pb, phase_b = Ex.single_of_spindet kib kjb in
match phase_a, phase_b with
| Phase.Pos, Phase.Pos
| Phase.Neg, Phase.Neg -> +. integral ha hb pa pb
| Phase.Neg, Phase.Pos
| Phase.Pos, Phase.Neg -> -. integral ha hb pa pb
end
| 2, 0 -> (* alpha double *)
begin
let h1, p1, h2, p2, phase = Ex.double_of_spindet kia kja in
match phase with
| Phase.Pos -> +. anti_integral h1 h2 p1 p2
| Phase.Neg -> -. anti_integral h1 h2 p1 p2
end
| 0, 2 -> (* beta double *)
begin
let h1, p1, h2, p2, phase = Ex.double_of_spindet kib kjb in
match phase with
| Phase.Pos -> +. anti_integral h1 h2 p1 p2
| Phase.Neg -> -. anti_integral h1 h2 p1 p2
end
| 1, 0 -> (* alpha single *)
begin
let h, p, phase = Ex.single_of_spindet kia kja in
match phase with
| Phase.Pos -> +. single h p kia kib
| Phase.Neg -> -. single h p kia kib
end
| 0, 1 -> (* beta single *)
begin
let h, p, phase = Ex.single_of_spindet kib kjb in
match phase with
| Phase.Pos -> +. single h p kib kia
| Phase.Neg -> -. single h p kib kia
end
| 0, 0 -> (* diagonal element *)
diag_element ()
| _ -> assert false
end
let make det_space =
let ndet = Determinant_space.size det_space in
let det = Determinant_space.determinants det_space in
let mo_basis = Determinant_space.mo_basis det_space in
let h_matrix = lazy (
Array.init ndet (fun i ->
let ki = det.(i) in
Array.init ndet (fun j ->
let kj = det.(j) in
h_ij mo_basis ki kj
)
)
|> Mat.of_array
)
in
let eigensystem = lazy (
Lazy.force h_matrix
|> Util.diagonalize_symm
)
in
{ det_space ; h_matrix ; eigensystem }