GQCP
Loading...
Searching...
No Matches
DysonOrbital.hpp
Go to the documentation of this file.
1// This file is part of GQCG-GQCP.
2//
3// Copyright (C) 2017-2020 the GQCG developers
4//
5// GQCG-GQCP is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// GQCG-GQCP is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with GQCG-GQCP. If not, see <http://www.gnu.org/licenses/>.
17
18#pragma once
19
20#include "ONVBasis/SpinResolvedONVBasis.hpp"
23
24
25namespace GQCP {
26
27
33template <typename _Scalar>
35public:
36 // The scalar type used for a single parameter: real or complex.
37 using Scalar = _Scalar;
38
39
40private:
41 // The Dyson amplitudes for the Dyson orbital. They are indicated as <N-1|a_p|N>, where 'p' is the index of spinor p.
42 VectorX<Scalar> m_amplitudes;
43
44
45public:
46 /*
47 * MARK: Constructors
48 */
49
53 DysonOrbital() = default;
54
60 DysonOrbital(const size_t K) :
61 m_amplitudes {VectorX<Scalar>::Zero(K)} {}
62
63
70 m_amplitudes {amplitudes} {}
71
72 /*
73 * MARK: Named constructors
74 */
75
84 template <typename Z = Scalar>
86
87 const auto onv_basis_J = linear_expansion_J.onvBasis();
88 const auto onv_basis_I = linear_expansion_I.onvBasis();
89
90 if ((onv_basis_J.alpha().numberOfElectrons() - onv_basis_I.alpha().numberOfElectrons() != 0) && (onv_basis_J.beta().numberOfElectrons() - onv_basis_I.beta().numberOfElectrons() != 1)) {
91 if ((onv_basis_J.alpha().numberOfElectrons() - onv_basis_I.alpha().numberOfElectrons() != 1) && (onv_basis_J.beta().numberOfElectrons() - onv_basis_I.beta().numberOfElectrons() != 0)) {
92 throw std::runtime_error("DysonOrbital::TransitionAmplitudes(LinearExpansion, LinearExpansion): linear_expansion_I is not expressed in a spin-resolved ONV basis with one fewer electron than linear_expansion_J.");
93 }
94 }
95
96 // Initialize environment variables.
97
98 // The 'passive' ONV basis is the ONV basis that is equal for both wave functions.
99 // The 'target' ONV basis has an electron difference of one.
100 // We initialize the variables for the case in which they differ in one beta electron, if this isn't the case, we will update it later.
101 auto passive_onv_basis_J = onv_basis_J.alpha();
102 auto passive_onv_basis_I = onv_basis_I.alpha();
103 auto target_onv_basis_J = onv_basis_J.beta();
104 auto target_onv_basis_I = onv_basis_I.beta();
105
106 // Mod variables relate to the modification of the address jump in coefficient index according to the ordering of the spin ONVs.
107 size_t passive_mod_J = target_onv_basis_J.dimension();
108 size_t passive_mod_I = target_onv_basis_I.dimension();
109 size_t target_mod = 1;
110
111 // If instead the ONV bases differ by one alpha electron we re-assign the variables to match the algorithm.
112 if ((onv_basis_J.alpha().numberOfElectrons() - onv_basis_I.alpha().numberOfElectrons() == 1) && (onv_basis_J.beta().numberOfElectrons() - onv_basis_I.beta().numberOfElectrons() == 0)) {
113 passive_onv_basis_J = target_onv_basis_J;
114 passive_onv_basis_I = target_onv_basis_I;
115 target_onv_basis_J = onv_basis_J.alpha();
116 target_onv_basis_I = onv_basis_I.alpha();
117
118 passive_mod_J = 1;
119 passive_mod_I = 1;
120 target_mod = passive_onv_basis_J.dimension();
121 }
122
123 const auto& ci_coeffs_J = linear_expansion_J.coefficients();
124 const auto& ci_coeffs_I = linear_expansion_I.coefficients();
125
126 VectorX<double> dyson_coeffs = VectorX<double>::Zero(onv_basis_J.alpha().numberOfOrbitals());
127
128 // The actual algorithm to determine the Dyson amplitudes.
129
130 // Since we want to calculate the overlap between two wave functions, the ONVs should have an equal number of electrons.
131 // We therefore iterate over the ONVs of the 'target' ONV basis, which all have an electron more, and annihilate in one of the orbitals (let the index of that orbital be called 'p').
132 // By calculating the overlap in the (N-1)-electron ONV basis, we can calculate the contributions to the 'p'-th coefficient (i.e. the Dyson amplitude) of the Dyson orbital.
133 SpinUnresolvedONV onv = target_onv_basis_J.constructONVFromAddress(0);
134
135 for (size_t Jt = 0; Jt < target_onv_basis_J.dimension(); Jt++) { // Jt loops over addresses of the target ONV basis.
136 int sign = -1; // Total phase factor of all the annihilations that have occurred.
137 for (size_t e = 0; e < target_onv_basis_J.numberOfElectrons(); e++) { // Loop over electrons in the ONV.
138
139 // Annihilate on the corresponding orbital, to make sure we can calculate overlaps in the (N-1)-'target' ONV basis.
140 sign *= -1;
141 size_t p = onv.occupationIndexOf(e);
142 onv.annihilate(p);
143
144 // Now, we calculate the overlap in the (N-1)-electron 'target' ONV basis.
145 // In order to access the correct coefficients for the, we need the address of the resulting (annihilated) ONV inside the 'target' (N-1)-electron ONV basis.
146 size_t address = target_onv_basis_I.addressOf(onv.unsignedRepresentation());
147
148 double coeff = 0;
149 for (size_t Jp = 0; Jp < passive_onv_basis_J.dimension(); Jp++) { // Jp loops over the addresses of the passive ONV basis.
150 coeff += sign * ci_coeffs_J(Jt * target_mod + Jp * passive_mod_J) * ci_coeffs_I(address * target_mod + Jp * passive_mod_I); // Access the indices of the coefficient vectors.
151 }
152 dyson_coeffs(p) += coeff;
153 onv.create(p); // Allow the iteration to continue with the original ONV.
154 }
155
156 if (Jt < target_onv_basis_J.dimension() - 1) { // Prevent the last permutation from occurring.
157 target_onv_basis_J.transformONVToNextPermutation(onv);
158 }
159 } // Target address (Jt) loop.
160
161 return DysonOrbital<Scalar>(dyson_coeffs);
162 }
163
172 template <typename Z = Scalar>
174
175 // Initialize environment variables.
176
177 const auto onv_basis_J = linear_expansion_J.onvBasis();
178 const auto onv_basis_I = linear_expansion_I.onvBasis();
179
180 if (onv_basis_J.numberOfElectrons() - onv_basis_I.numberOfElectrons() != 1) {
181 throw std::runtime_error("DysonOrbital::TransitionAmplitudes(LinearExpansion, LinearExpansion): linear_expansion_I is not expressed in a spin-unresolved ONV basis with one fewer electron than linear_expansion_J.");
182 }
183
184 const auto& ci_coeffs_J = linear_expansion_J.coefficients();
185 const auto& ci_coeffs_I = linear_expansion_I.coefficients();
186
187 VectorX<double> dyson_coeffs = VectorX<double>::Zero(onv_basis_J.numberOfOrbitals());
188
189 // The actual algorithm to determine the Dyson amplitudes.
190
191 // Since we want to calculate the overlap between two wave functions, the ONVs should have an equal number of electrons.
192 // We therefore iterate over the ONVs of the N-electron ONV basis, which all have an electron more, and annihilate in one of the orbitals (let the index of that orbital be called 'p').
193 // By calculating the overlap in the (N-1)-electron ONV basis, we can calculate the contributions to the 'p'-th coefficient (i.e. the Dyson amplitude) of the Dyson orbital.
194 SpinUnresolvedONV onv = onv_basis_J.constructONVFromAddress(0);
195 double coeff = 0;
196
197 for (size_t J = 0; J < onv_basis_J.dimension(); J++) { // J loops over addresses of the N-electron spin-unresolved ONV basis.
198 int sign = -1; // Total phase factor of all the annihilations that have occurred.
199 for (size_t e = 0; e < onv_basis_J.numberOfElectrons(); e++) { // Loop over electrons in the ONV.
200
201 // Annihilate on the corresponding orbital, to make sure we can calculate overlaps in the (N-1)-electron ONV basis.
202 sign *= -1;
203 size_t p = onv.occupationIndexOf(e);
204 onv.annihilate(p);
205
206 // Now, we calculate the overlap in the (N-1)-electron ONV basis.
207 // In order to access the correct coefficients for the, we need the address of the resulting (annihilated) ONV inside the (N-1)-electron ONV basis.
208 size_t address = onv_basis_I.addressOf(onv.unsignedRepresentation());
209
210 coeff = sign * ci_coeffs_J(J) * ci_coeffs_I(address); // Access the indices of the coefficient vectors.
211
212 dyson_coeffs(p) += coeff;
213 onv.create(p); // Allow the iteration to continue with the original ONV.
214 }
215
216 if (J < onv_basis_J.dimension() - 1) { // Prevent the last permutation from occurring.
217 onv_basis_J.transformONVToNextPermutation(onv);
218 }
219 } // Loop over J ONVs.
220
221 return DysonOrbital<Scalar>(dyson_coeffs);
222 }
223
224 /*
225 * MARK: Access
226 */
227
231 const VectorX<Scalar>& amplitudes() const { return this->m_amplitudes; }
232};
233
234} // namespace GQCP
Definition: DysonOrbital.hpp:34
DysonOrbital(const VectorX< Scalar > &amplitudes)
Definition: DysonOrbital.hpp:69
static enable_if_t< std::is_same< Z, double >::value, DysonOrbital< double > > TransitionAmplitudes(const LinearExpansion< double, SpinResolvedONVBasis > &linear_expansion_J, const LinearExpansion< double, SpinResolvedONVBasis > &linear_expansion_I)
Definition: DysonOrbital.hpp:85
_Scalar Scalar
Definition: DysonOrbital.hpp:37
DysonOrbital()=default
DysonOrbital(const size_t K)
Definition: DysonOrbital.hpp:60
const VectorX< Scalar > & amplitudes() const
Definition: DysonOrbital.hpp:231
static enable_if_t< std::is_same< Z, double >::value, DysonOrbital< double > > TransitionAmplitudes(const LinearExpansion< double, SpinUnresolvedONVBasis > &linear_expansion_J, const LinearExpansion< double, SpinUnresolvedONVBasis > &linear_expansion_I)
Definition: DysonOrbital.hpp:173
Definition: LinearExpansion.hpp:59
const VectorX< Scalar > & coefficients() const
Definition: LinearExpansion.hpp:393
const ONVBasis & onvBasis() const
Definition: LinearExpansion.hpp:398
Definition: Matrix.hpp:47
Definition: BaseOneElectronIntegralBuffer.hpp:25
typename std::enable_if< B, T >::type enable_if_t
Definition: type_traits.hpp:37