10
1
mirror of https://gitlab.com/scemama/QCaml.git synced 2025-01-03 10:05:40 +01:00
This commit is contained in:
Anthony Scemama 2024-07-30 12:55:42 +02:00
parent a9c5fd16a9
commit 5f47e96962
10 changed files with 1859 additions and 115 deletions

1183
ci/lib/ci.ml Normal file

File diff suppressed because it is too large Load Diff

283
ci/lib/ci_matrix_element.ml Normal file
View File

@ -0,0 +1,283 @@
open Common
module De = Determinant
module Ex = Excitation
module Sp = Spindeterminant
type t = float list
(** Computes non-zero integral values.
@param integrals A list of tuples containing one-electron, two-electron integrals, and optionally three-electron integrals.
@param degree_a The degree of excitation in alpha spin orbitals.
@param degree_b The degree of excitation in beta spin orbitals.
@param ki The initial Slater determinant.
@param kj The final Slater determinant.
@return TODO A list of computed integral values based on the specified degrees and molecular orbitals.
This function performs the following operations:
- Converts spin determinants to lists of molecular orbitals for alpha and beta spins.
- Defines helper functions for singly and doubly excited determinants, and diagonal elements
- Uses lazy evaluation to defer computations until required.
- Supports both two-electron and three-electron integrals, handling phase factors appropriately.
Example usage:
{[
let integrals = [(one_e_integral, two_e_integral, None); (one_e_integral, two_e_integral, Some three_e_integral)] in
let result = non_zero integrals 1 1 ki kj
]}
*)
let non_zero integrals degree_a degree_b ki kj =
let kia = De.alfa ki and kib = De.beta ki
and kja = De.alfa kj and kjb = De.beta kj
in
let single h p spin same opposite =
let same_spin_mo_list =
Sp.to_list same
and opposite_spin_mo_list =
Sp.to_list opposite
in
fun one_e two_e ->
let same_spin =
List.fold_left (fun accu i -> accu +. two_e h i p i spin spin) 0. same_spin_mo_list
and opposite_spin =
List.fold_left (fun accu i -> accu +. two_e h i p i spin (Spin.other spin) ) 0. opposite_spin_mo_list
in (one_e h p spin) +. same_spin +. opposite_spin
in
let diag_element =
let mo_a = Sp.to_list kia
and mo_b = Sp.to_list kib
in
fun one_e two_e ->
let one =
(List.fold_left (fun accu i -> accu +. one_e i i Spin.Alfa) 0. mo_a)
+.
(List.fold_left (fun accu i -> accu +. one_e i i Spin.Beta) 0. mo_b)
in
let two =
let rec aux_same spin accu = function
| [] -> accu
| i :: rest ->
let new_accu =
List.fold_left (fun accu j -> accu +. two_e i j i j spin spin) accu rest
in
(aux_same [@tailcall]) spin 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_e i j i j Spin.Alfa Spin.Beta) accu other
in
(aux_opposite [@tailcall]) new_accu other rest
in
(aux_same Spin.Alfa 0. mo_a) +. (aux_same Spin.Beta 0. mo_b) +.
(aux_opposite 0. mo_a mo_b)
in
one +. two
in
let result_2e = lazy (
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 -> fun _ two_e -> two_e ha hb pa pb Spin.Alfa Spin.Beta
| Phase.Neg, Phase.Pos
| Phase.Pos, Phase.Neg -> fun _ two_e -> -. two_e ha hb pa pb Spin.Alfa Spin.Beta
end
| 2, 0 -> (* alpha double *)
begin
let h1, p1, h2, p2, phase = Ex.double_of_spindet kia kja in
match phase with
| Phase.Pos -> fun _ two_e -> two_e h1 h2 p1 p2 Spin.Alfa Spin.Alfa
| Phase.Neg -> fun _ two_e -> -. two_e h1 h2 p1 p2 Spin.Alfa Spin.Alfa
end
| 0, 2 -> (* beta double *)
begin
let h1, p1, h2, p2, phase = Ex.double_of_spindet kib kjb in
match phase with
| Phase.Pos -> fun _ two_e -> two_e h1 h2 p1 p2 Spin.Beta Spin.Beta
| Phase.Neg -> fun _ two_e -> -. two_e h1 h2 p1 p2 Spin.Beta Spin.Beta
end
| 1, 0 -> (* alpha single *)
begin
let h, p, phase = Ex.single_of_spindet kia kja in
match phase with
| Phase.Pos -> fun one_e two_e -> single h p Spin.Alfa kia kib one_e two_e
| Phase.Neg -> fun one_e two_e -> -. single h p Spin.Alfa kia kib one_e two_e
end
| 0, 1 -> (* beta single *)
begin
let h, p, phase = Ex.single_of_spindet kib kjb in
match phase with
| Phase.Pos -> fun one_e two_e -> single h p Spin.Beta kib kia one_e two_e
| Phase.Neg -> fun one_e two_e -> -. single h p Spin.Beta kib kia one_e two_e
end
| 0, 0 -> (* diagonal element *)
diag_element
| _ -> assert false
) in
let result_3e = lazy (
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 -> fun _ two_e _ -> two_e ha hb pa pb Spin.Alfa Spin.Beta
| Phase.Neg, Phase.Pos
| Phase.Pos, Phase.Neg -> fun _ two_e _ -> -. two_e ha hb pa pb Spin.Alfa Spin.Beta
end
| 2, 0 -> (* alpha double *)
begin
let h1, p1, h2, p2, phase = Ex.double_of_spindet kia kja in
match phase with
| Phase.Pos -> fun _ two_e _ -> two_e h1 h2 p1 p2 Spin.Alfa Spin.Alfa
| Phase.Neg -> fun _ two_e _ -> -. two_e h1 h2 p1 p2 Spin.Alfa Spin.Alfa
end
| 0, 2 -> (* beta double *)
begin
let h1, p1, h2, p2, phase = Ex.double_of_spindet kib kjb in
match phase with
| Phase.Pos -> fun _ two_e _ -> two_e h1 h2 p1 p2 Spin.Beta Spin.Beta
| Phase.Neg -> fun _ two_e _ -> -. two_e h1 h2 p1 p2 Spin.Beta Spin.Beta
end
| 1, 0 -> (* alpha single *)
begin
let h, p, phase = Ex.single_of_spindet kia kja in
match phase with
| Phase.Pos -> fun one_e two_e _ -> single h p Spin.Alfa kia kib one_e two_e
| Phase.Neg -> fun one_e two_e _ -> -. single h p Spin.Alfa kia kib one_e two_e
end
| 0, 1 -> (* beta single *)
begin
let h, p, phase = Ex.single_of_spindet kib kjb in
match phase with
| Phase.Pos -> fun one_e two_e _ -> single h p Spin.Beta kib kia one_e two_e
| Phase.Neg -> fun one_e two_e _ -> -. single h p Spin.Beta kib kia one_e two_e
end
| 0, 0 -> (* diagonal element *)
fun one_e two_e _ -> diag_element one_e two_e
| 3, 0 -> (* alpha triple *)
begin
let h1, p1, h2, p2, h3, p3, phase = Ex.triple_of_spindet kia kja in
match phase with
| Phase.Pos -> fun _ _ three_e -> three_e h1 h2 h3 p1 p2 p3 Spin.Alfa Spin.Alfa Spin.Alfa
| Phase.Neg -> fun _ _ three_e -> -. three_e h1 h2 h3 p1 p2 p3 Spin.Alfa Spin.Alfa Spin.Alfa
end
| 0, 3 -> (* beta triple *)
begin
let h1, p1, h2, p2, h3, p3, phase = Ex.triple_of_spindet kib kja in
match phase with
| Phase.Pos -> fun _ _ three_e -> three_e h1 h2 h3 p1 p2 p3 Spin.Beta Spin.Beta Spin.Beta
| Phase.Neg -> fun _ _ three_e -> -. three_e h1 h2 h3 p1 p2 p3 Spin.Beta Spin.Beta Spin.Beta
end
| 2, 1 -> (* alpha2 beta triple *)
begin
let h1, p1, h2, p2, phase = Ex.double_of_spindet kia kja in
let h3, p3, phase' = Ex.single_of_spindet kib kjb in
match phase, phase' with
| Phase.Pos, Phase.Pos
| Phase.Neg, Phase.Neg ->
fun _ _ three_e -> three_e h1 h2 h3 p1 p2 p3 Spin.Alfa Spin.Alfa Spin.Beta
| Phase.Neg, Phase.Pos
| Phase.Pos, Phase.Neg ->
fun _ _ three_e -> -. three_e h1 h2 h3 p1 p2 p3 Spin.Alfa Spin.Alfa Spin.Beta
end
| 1, 2 -> (* alpha beta2 triple *)
begin
let h1, p1, phase = Ex.single_of_spindet kia kja in
let h2, p2, h3, p3, phase' = Ex.double_of_spindet kib kjb in
match phase, phase' with
| Phase.Pos, Phase.Pos
| Phase.Neg, Phase.Neg ->
fun _ _ three_e -> three_e h1 h2 h3 p1 p2 p3 Spin.Alfa Spin.Beta Spin.Beta
| Phase.Neg, Phase.Pos
| Phase.Pos, Phase.Neg ->
fun _ _ three_e -> -. three_e h1 h2 h3 p1 p2 p3 Spin.Alfa Spin.Beta Spin.Beta
end
| _ -> fun _ _ _ -> 0.
) in
List.map (fun (one_e, two_e, x) ->
match x with
| None -> (Lazy.force result_2e) one_e two_e
| Some three_e -> (Lazy.force result_3e) one_e two_e three_e
) integrals
let make integrals ki kj =
let degree_a, degree_b =
De.excitation_levels ki kj
in
if degree_a+degree_b > 2 then
List.map (fun _ -> 0.) integrals
else
non_zero integrals degree_a degree_b ki kj
let make_s2 ki kj =
let degree_a = De.excitation_level_alfa ki kj in
let kia = De.alfa ki in
let kja = De.alfa kj in
if degree_a > 1 then 0.
else
let degree_b = De.excitation_level_beta ki kj in
let kib = De.beta ki in
let kjb = De.beta kj in
match degree_a, degree_b with
| 1, 1 -> (* alpha-beta double *)
let ha, pa, phase_a = Ex.single_of_spindet kia kja in
let hb, pb, phase_b = Ex.single_of_spindet kib kjb in
if ha = pb && hb = pa then
begin
match phase_a, phase_b with
| Phase.Pos, Phase.Pos
| Phase.Neg, Phase.Neg -> -1.
| Phase.Neg, Phase.Pos
| Phase.Pos, Phase.Neg -> 1.
end
else 0.
| 0, 0 ->
let ba = Sp.bitstring kia and bb = Sp.bitstring kib in
let tmp = Bitstring.logxor ba bb in
let n_a = Bitstring.logand ba tmp |> Bitstring.popcount in
let n_b = Bitstring.logand bb tmp |> Bitstring.popcount in
let s_z = 0.5 *. float_of_int (n_a - n_b) in
float_of_int n_a +. s_z *. (s_z -. 1.)
| _ -> 0.

View File

@ -0,0 +1,67 @@
open Common
module De = Determinant
type t
val make :
((int -> int -> Spin.t -> float) *
(int -> int -> int -> int -> Spin.t -> Spin.t -> float) *
(int -> int -> int -> int -> int -> int -> Spin.t -> Spin.t -> Spin.t -> float) option) list ->
De.t -> De.t -> float list
(** [make integrals ki kj] Computes matrix elements for multiple operators between two Slater
determinants, or returns zeros if the total degree of excitation exceeds 2.
@param integrals A list of tuples containing one-electron, two-electron
integrals, and optionally three-electron integrals, for each operator
@param ki The initial Slater determinant.
@param kj The final Slater determinant.
@return A list of computed matrix elements or zeroes if the total excitation
lebel (degree_a + degree_b) is greater than 2.
Example usage:
{[
let integrals = [(one_e_integral, two_e_integral, None); (one_e_integral', two_e_integral', Some three_e_integral')] in
let result = make integrals ki kj
]}
*)
val make_s2 : De.t -> De.t -> float
(** [make_s2 ki kj] computes the value of the $S^2$ operator for two determinants.
@param ki The initial spin determinant.
@param kj The final spin determinant.
@return The computed value of the $S^2$ operator.
Example usage:
{[
let s2_value = make_s2 ki kj
]}
*)
(** Computes matrix elements when the user knows they are non-zero.
@param integrals A list of tuples containing one-electron, two-electron integrals, and optionally three-electron integrals.
@param degree_a The degree of excitation in alpha spin orbitals.
@param degree_b The degree of excitation in beta spin orbitals.
@param ki The initial Slater determinant.
@param kj The final Slater determinant.
@return A list of matrix elements for multiple operators
Example usage:
{[
let integrals = [(one_e_integral, two_e_integral, None); (one_e_integral, two_e_integral, Some three_e_integral)] in
let result = non_zero integrals 1 1 ki kj
]}
*)
val non_zero :
((int -> int -> Spin.t -> float) *
(int -> int -> int -> int -> Spin.t -> Spin.t -> float) *
(int -> int -> int -> int -> int -> int -> Spin.t -> Spin.t -> Spin.t -> float) option) list ->
int -> int -> De.t -> De.t -> float list

View File

@ -0,0 +1,162 @@
open Common
let (%.) = Vector.(%.)
let (%:) = Matrix.(%:)
type t
let make
?guess
?(n_states=1)
?(n_iter=8)
?(threshold=1.e-7)
diagonal
matrix_prod
=
(* Size of the matrix to diagonalize *)
let n = Vector.dim diagonal in
let m = n_states in
(* Create guess vectors u, with unknown vectors initialized to unity. *)
let init_vectors m =
let result = Matrix.make n m 0. in
for i=0 to m-1 do
Matrix.set result i i 1.;
done;
result
in
let guess =
match guess with
| Some vectors -> vectors
| None -> init_vectors m
in
let guess =
if Matrix.dim2 guess = n_states then guess
else
(Matrix.to_col_vecs_list guess) @
(Matrix.to_col_vecs_list (init_vectors (m-(Matrix.dim2 guess))) )
|> Matrix.of_col_vecs_list
in
let pick_new u =
Matrix.to_col_vecs_list u
|> Util.list_pack m
|> List.rev
|> List.hd
in
let u_new = Matrix.to_col_vecs_list guess in
let rec iteration u u_new w iter macro =
(* u is a list of orthonormal vectors, on which the operator has
been applied : w = op.u
u_new is a list of vectors which will increase the size of the
space.
*)
(* Orthonormalize input vectors u_new *)
let u_new_ortho =
List.concat [u ; u_new]
|> Matrix.of_col_vecs_list
|> Orthonormalization.qr_ortho
|> pick_new
in
(* Apply the operator to the m last vectors *)
let w_new =
matrix_prod (
u_new_ortho
|> Matrix.of_col_vecs_list)
|> Matrix.to_col_vecs_list
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 =
Matrix.of_col_vecs_list u_next
and m_W =
Matrix.of_col_vecs_list w_next
in
let m_h =
Matrix.gemm_tn m_U m_W
in
(* Diagonalize h *)
let y, lambda =
Matrix.diagonalize_symm m_h
in
(* Express m lowest eigenvectors of h in the large basis *)
let m_new_U =
Matrix.gemm ~n:m m_U y
and m_new_W =
Matrix.gemm ~n:m m_W y
in
(* Compute the residual as proposed new vectors *)
let u_proposed =
Matrix.init_cols n m (fun i k ->
let delta = lambda%.(k) -. diagonal%.(i) in
let delta =
if abs_float delta > 1.e-2 then delta
else if delta > 0. then 1.e-2
else (-1.e-2)
in
(lambda%.(k) *. (m_new_U%:(i,k)) -. (m_new_W%:(i,k)) ) /. delta
)
|> Matrix.to_col_vecs_list
in
let residual_norms = List.rev @@ List.rev_map Vector.norm u_proposed in
let residual_norm =
List.fold_left (fun accu i -> accu +. i *. i) 0. residual_norms
|> sqrt
in
Printf.printf "%3d " iter;
Vector.iteri (fun i x -> if (i<=m) then Printf.printf "%16.10f " x) lambda;
Printf.printf "%16.8e%!\n" residual_norm;
(* Make new vectors sparse *)
let u_proposed =
Matrix.of_col_vecs_list u_proposed
in
let maxu = Matrix.norm ~l:`Linf u_proposed in
let thr = maxu *. 0.00001 in
let u_proposed =
Matrix.map (fun x -> if abs_float x < thr then 0. else x) u_proposed
|> Matrix.to_col_vecs_list
in
(*
Format.printf "%a@." Matrix.pp_matrix @@ m_new_U;
*)
if residual_norm > threshold && macro > 0 then
let u_next, w_next, iter, macro =
if iter = n_iter then
m_new_U |> pick_new,
m_new_W |> pick_new,
0, (macro-1)
else
u_next, w_next, (iter+1), macro
in
(iteration [@tailcall]) u_next u_proposed w_next iter macro
else
(m_new_U |> pick_new |> Matrix.of_col_vecs_list), lambda
in
iteration [] u_new [] 1 30

View File

@ -0,0 +1,21 @@
type t
val make :
?guess:('a, 'b) Matrix.t ->
?n_states:int ->
?n_iter:int ->
?threshold:float ->
'a Vector.t ->
(('a, 'b) Matrix.t -> ('a, 'b) Matrix.t) ->
('a, 'b) Matrix.t * 'b Vector.t
(** Performs a Davidson diagonalization. Example:
let eigenvectors, eigenvalues =
Davidson.make diagonal mat_prod
- [diagonal] contains the diagonal of the matrix to diagonalize
- [mat_prod] is a function performing a matrix multiplication of the matrix to
diagonalize with the matrix containing the current set of vectors
*)

View File

@ -8,3 +8,4 @@ module Matrix = Matrix
module Orthonormalization = Orthonormalization module Orthonormalization = Orthonormalization
module Spherical_to_cartesian = Spherical_to_cartesian module Spherical_to_cartesian = Spherical_to_cartesian
module Vector = Vector module Vector = Vector
module Davidson = Davidson

View File

@ -233,6 +233,13 @@ let to_array a =
done; done;
result result
let norm ?(l=`L2) t =
match l with
| `L2 -> lange ~norm:`F t
| `L1 -> lange ~norm:`O t
| `Linf -> lange ~norm:`I t
let normalize_mat_inplace t = let normalize_mat_inplace t =
let norm = Mat.as_vec t |> nrm2 in let norm = Mat.as_vec t |> nrm2 in
Mat.scal norm t Mat.scal norm t
@ -344,6 +351,16 @@ let copy ?m ?n ?br ?bc ?ar ?ac a =
let copy_inplace ?m ?n ?br ?bc ~b ?ar ?ac a = let copy_inplace ?m ?n ?br ?bc ~b ?ar ?ac a =
ignore @@ lacpy ?m ?n ?br ?bc ~b ?ar ?ac a ignore @@ lacpy ?m ?n ?br ?bc ~b ?ar ?ac a
let copy_row ?vec a i =
let result =
match vec with
| None -> Mat.copy_row a i
| Some v ->
let vec = Vector.to_bigarray_inplace v in
Mat.copy_row ~vec a i
in
Vector.of_bigarray_inplace result
let scale_cols_inplace a v = let scale_cols_inplace a v =
Vector.to_bigarray_inplace v Vector.to_bigarray_inplace v
|> Mat.scal_cols a |> Mat.scal_cols a

View File

@ -71,6 +71,9 @@ val div : ('a,'b) t -> ('a,'b) t -> ('a,'b) t
val amax : ('a,'b) t -> float val amax : ('a,'b) t -> float
(** Maximum of the absolute values of the elements of the matrix. *) (** Maximum of the absolute values of the elements of the matrix. *)
val norm : ?l:[< `L1 | `L2 | `Linf > `L2 ] -> ('a,'b) t -> float
(** $L^1$, $L^2$ or $L^\infty$ norm of the matrix *)
val add_inplace : c:('a,'b) t -> ('a,'b) t -> ('a,'b) t -> unit val add_inplace : c:('a,'b) t -> ('a,'b) t -> ('a,'b) t -> unit
(** [add_inplace c a b] : performs [c = a+b] in-place. *) (** [add_inplace c a b] : performs [c = a+b] in-place. *)
@ -118,10 +121,13 @@ val of_array : float array array -> ('a,'b) t
(** Converts an array of arrays into a matrix *) (** Converts an array of arrays into a matrix *)
val copy: ?m:int -> ?n:int -> ?br:int -> ?bc:int -> ?ar:int -> ?ac:int -> ('a,'b) t -> ('a,'b) t val copy: ?m:int -> ?n:int -> ?br:int -> ?bc:int -> ?ar:int -> ?ac:int -> ('a,'b) t -> ('a,'b) t
(** Copies all or part of a two-dimensional matrix A to a new matrix B *) (** Copies all or part of a matrix A to a new matrix B *)
val copy_inplace: ?m:int -> ?n:int -> ?br:int -> ?bc:int -> b:('a,'b) t -> ?ar:int -> ?ac:int -> ('a,'b) t -> unit val copy_inplace: ?m:int -> ?n:int -> ?br:int -> ?bc:int -> b:('a,'b) t -> ?ar:int -> ?ac:int -> ('a,'b) t -> unit
(** Copies all or part of a two-dimensional matrix A to an existing matrix B *) (** Copies all or part of a matrix A to an existing matrix B *)
val copy_row : ?vec:('b Vector.t) -> ('a,'b) t -> int -> 'b Vector.t
(** Copies a given row of a matrix into a vector *)
(* (*
val col: ('a,'b) t -> int -> 'a Vector.t val col: ('a,'b) t -> int -> 'a Vector.t

View File

@ -4,7 +4,11 @@ type 'a t = Vec.t
let relabel t = t let relabel t = t
let copy ?n ?ofsy ?incy ?y ?ofsx ?incx t = copy ?n ?ofsy ?incy ?y ?ofsx ?incx t let copy ?n ?ofsy ?incy ?y ?ofsx ?incx t = copy ?n ?ofsy ?incy ?y ?ofsx ?incx t
let norm t = nrm2 t let norm ?(l=`L2) t =
match l with
| `L2 -> nrm2 t
| `L1 -> asum t
| `Linf -> amax t
let dim t = Vec.dim t let dim t = Vec.dim t
let neg t = Vec.neg t let neg t = Vec.neg t

View File

@ -31,8 +31,8 @@ val div : 'a t -> 'a t -> 'a t
val dot : 'a t -> 'a t -> float val dot : 'a t -> 'a t -> float
(** [dot v1 v2] : Dot product between v1 and v2 *) (** [dot v1 v2] : Dot product between v1 and v2 *)
val norm : 'a t -> float val norm : ?l:[< `L1 | `L2 | `Linf > `L2 ] -> 'a t -> float
(** Norm of the vector : %{ ||v|| = $\sqrt{v.v}$ %} *) (** $L^1$, $L^2$ or $L^\infty$ norm of the vector *)
val sqr : 'a t -> 'a t val sqr : 'a t -> 'a t
(** [sqr t = map (fun x -> x *. x) t] *) (** [sqr t = map (fun x -> x *. x) t] *)