GQCP
Loading...
Searching...
No Matches
SimpleSQOneElectronOperator.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
25
26
27namespace GQCP {
28
29
39template <typename _Scalar, typename _Vectorizer, typename _DerivedOperator>
41 public SQOperatorStorage<SquareMatrix<_Scalar>, _Vectorizer, SimpleSQOneElectronOperator<_Scalar, _Vectorizer, _DerivedOperator>>,
42 public BasisTransformable<_DerivedOperator>,
43 public JacobiRotatable<_DerivedOperator> {
44public:
45 // The scalar type used for a single parameter: real or complex.
46 using Scalar = _Scalar;
47
48 // The type of the vectorizer that relates a one-dimensional storage of matrices to the tensor structure of one-electron operators. This allows for a distinction between scalar operators (such as the kinetic energy operator), vector operators (such as the spin operator) and matrix/tensor operators (such as quadrupole and multipole operators).
49 using Vectorizer = _Vectorizer;
50
51 // The type of the operator that derives from this class, enabling CRTP and compile-time polymorphism.
52 using DerivedOperator = _DerivedOperator;
53
54 // The matrix representation of the parameters of (one of the components of) the one-electron operator.
56
57 // The type of 'this'.
59
60 // The type that corresponds to the scalar version of the derived one-electron operator type.
62
63 // The type of transformation that is naturally associated to the derived one-electron operator.
65
66 // The type of the one-particle density matrix that is naturally associated to the derived one-electron operator.
68
69 // The type of the two-particle density matrix that is naturally associated to the derived one-electron operator.
71
72 // The type used to encapsulate the Mulliken domain.
74
75
76public:
77 /*
78 * MARK: Constructors
79 */
80
81 // Inherit `SQOperatorStorage`'s constructors.
83
84
85 /*
86 * MARK: Calculations
87 */
88
97
98 if (this->numberOfOrbitals() != D.numberOfOrbitals()) {
99 throw std::invalid_argument("SimpleSQOneElectronOperator::calculateExpectationValue(const Derived1DM<Scalar>&): The given 1-DM's dimension is not compatible with the one-electron operator.");
100 }
101
102 Tensor<Scalar, 2> D_tensor = Eigen::TensorMap<Eigen::Tensor<const Scalar, 2>>(D.matrix().data(), this->numberOfOrbitals(), this->numberOfOrbitals());
103
104 // Calculate the expectation value for every component of the operator.
105 const auto& parameters = this->allParameters();
106 std::vector<Scalar> expectation_values(this->numberOfComponents()); // Zero-initialize the vector with a number of elements.
107 for (size_t i = 0; i < this->numberOfComponents(); i++) {
108
109 // Perform the contraction "f(p, q) D(p, q)".
110 Tensor<Scalar, 2> f_tensor = Eigen::TensorMap<Eigen::Tensor<const Scalar, 2>>(parameters[i].data(), this->numberOfOrbitals(), this->numberOfOrbitals());
111 Eigen::Tensor<Scalar, 0> contraction = f_tensor.template einsum<2>("pq, pq->", D_tensor);
112
113 // As the contraction is a scalar (a tensor of rank 0), we should access using `operator(0)`.
114 expectation_values[i] = contraction(0);
115 }
116
117 return StorageArray<Scalar, Vectorizer> {expectation_values, this->array.vectorizer()};
118 }
119
120
131 template <typename Z = Scalar>
133
134 if (DM.numberOfOrbitals() != this->numberOfOrbitals()) {
135 throw std::invalid_argument("SimpleSQOneElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The 1-DM's dimensions are not compatible with this one-electron operator.");
136 }
137
138 if (dm.numberOfOrbitals() != this->numberOfOrbitals()) {
139 throw std::invalid_argument("SimpleSQOneElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The 2-DM's dimensions are not compatible with this one-electron operator.");
140 }
141
142 const auto& parameters = this->allParameters(); // The parameters of the one-electron operator, as a vector.
143 std::vector<SquareMatrix<double>> F_vector {this->numberOfComponents()}; // The resulting vector of Fockian matrices.
144
145 // A KISS implementation of the calculation of the Fockian matrix.
146 const auto& D = DM.matrix();
147 for (size_t i = 0; i < this->numberOfComponents(); i++) {
148 const auto& f_i = parameters[i]; // The matrix representation of the parameters of the i-th component.
149
150 // Calculate the Fockian matrix for every component and add it to the vector.
151 SquareMatrix<double> F_i = SquareMatrix<double>::Zero(this->numberOfOrbitals()); // The Fockian matrix of the i-th component.
152 for (size_t p = 0; p < this->numberOfOrbitals(); p++) {
153 for (size_t q = 0; q < this->numberOfOrbitals(); q++) {
154
155 for (size_t r = 0; r < this->numberOfOrbitals(); r++) {
156 F_i(p, q) += f_i(q, r) * 0.5 * (D(p, r) + D(r, p)); // Include a factor 1/2 to accommodate for response density matrices.
157 }
158 }
159 } // F_i elements loop
160 F_vector[i] = F_i;
161 }
162
164 }
165
166
177 template <typename Z = Scalar>
179
180 if (DM.numberOfOrbitals() != this->numberOfOrbitals()) {
181 throw std::invalid_argument("SimpleSQOneElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The given 1-DM's dimensions are not compatible with this one-electron operator.");
182 }
183
184 if (dm.numberOfOrbitals() != this->numberOfOrbitals()) {
185 throw std::invalid_argument("SimpleSQOneElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The given 2-DM's dimensions are not compatible with this one-electron operator.");
186 }
187
188
189 const auto& parameters = this->allParameters(); // The parameters of the one-electron operator, as a vector.
190 std::vector<SquareRankFourTensor<double>> G_vector {this->numberOfComponents()}; // The resulting vector of super-Fockian matrices.
191 const auto F_vector = this->calculateFockianMatrix(DM, dm).elements(); // The Fockian matrices are necessary in the calculation of the super-Fockian matrices.
192
193 // A KISS implementation of the calculation of the super-Fockian matrix.
194 const auto& D = DM.matrix();
195 for (size_t i = 0; i < this->numberOfComponents(); i++) {
196
197 const auto& f_i = parameters[i]; // The matrix representation of the parameters of the i-th component.
198 const auto& F_i = F_vector[i]; // The Fockian matrix of the i-th component.
199
200 // Calculate the super-Fockian matrix for every component and add it to the array. Add factors 1/2 to accommodate for response density matrices.
202 G_i.setZero();
203 for (size_t p = 0; p < this->numberOfOrbitals(); p++) {
204 for (size_t q = 0; q < this->numberOfOrbitals(); q++) {
205 for (size_t r = 0; r < this->numberOfOrbitals(); r++) {
206 for (size_t s = 0; s < this->numberOfOrbitals(); s++) {
207
208 if (q == r) {
209 G_i(p, q, r, s) += F_i(p, s);
210 }
211
212 G_i(p, q, r, s) -= f_i(s, p) * 0.5 * (D(r, q) + D(q, r));
213 }
214 }
215 }
216 } // G_i elements loop
217 G_vector[i] = G_i;
218 }
219
221 }
222
223
224 /*
225 * MARK: Conforming to BasisTransformable
226 */
227
235 DerivedOperator transformed(const Transformation& T) const override {
236
237 // Calculate the basis transformation for every component of the operator.
238 const auto& parameters = this->allParameters();
239 auto result = this->allParameters();
240
241 for (size_t i = 0; i < this->numberOfComponents(); i++) {
242 const auto& f_i = parameters[i];
243
244 result[i] = T.matrix().adjoint() * f_i * T.matrix();
245 }
246
248 }
249
250
251 // Allow the `rotate` method from `BasisTransformable`, since there's also a `rotate` from `JacobiRotatable`.
253
254 // Allow the `rotated` method from `BasisTransformable`, since there's also a `rotated` from `JacobiRotatable`.
256
257
258 /*
259 * MARK: Conforming to JacobiRotatable
260 */
261
269 DerivedOperator rotated(const JacobiRotation& jacobi_rotation) const override {
270
271 // Use Eigen's Jacobi module to apply the Jacobi rotations directly (cfr. T.adjoint() * M * T).
272 const auto p = jacobi_rotation.p();
273 const auto q = jacobi_rotation.q();
274 const auto jacobi_rotation_eigen = jacobi_rotation.Eigen();
275
276 // Calculate the basis transformation for every component of the operator.
277 auto result = this->allParameters();
278 for (size_t i = 0; i < this->numberOfComponents(); i++) {
279 result[i].applyOnTheLeft(p, q, jacobi_rotation_eigen.adjoint());
280 result[i].applyOnTheRight(p, q, jacobi_rotation_eigen);
281 }
282
284 }
285
286 // Allow the `rotate` method from `JacobiRotatable`, since there's also a `rotate` from `BasisTransformable`.
288
289
290 /*
291 * MARK: One-index transformations
292 */
293
302
303 // Calculate the basis transformation for every component of the operator.
304 const auto& parameters = this->allParameters();
305 auto result = this->allParameters();
306
307 for (size_t i = 0; i < this->numberOfComponents(); i++) {
308 const auto& f_i = parameters[i];
309
310 result[i] = T.matrix().adjoint() * f_i + f_i * T.matrix();
311 }
312
314 }
315
316
317 /*
318 * MARK: Mulliken Domain
319 */
320
328 DerivedOperator partitioned(const Transformation& mulliken_projection_matrix) const { return 0.5 * this->oneIndexTransformed(mulliken_projection_matrix); }
329};
330
331
332/*
333 * MARK: Operator traits
334 */
335
339template <typename _Scalar, typename _Vectorizer, typename _DerivedOperator>
340struct OperatorTraits<SimpleSQOneElectronOperator<_Scalar, _Vectorizer, _DerivedOperator>> {
341
342 // The scalar type used for a single parameter/matrix element/integral: real or complex.
343 using Scalar = _Scalar;
344
345 // The type of the vectorizer that relates a one-dimensional storage of matrices to the tensor structure of one-electron operators. This allows for a distinction between scalar operators (such as the kinetic energy operator), vector operators (such as the spin operator) and matrix/tensor operators (such as quadrupole and multipole operators).
346 using Vectorizer = _Vectorizer;
347
348 // The type of the operator that derives from `SimpleSQOneElectronOperator`, enabling CRTP and compile-time polymorphism.
349 using DerivedOperator = _DerivedOperator;
350};
351
352
353} // namespace GQCP
Definition: BasisTransformable.hpp:50
void rotate(const Transformation &U)
Definition: BasisTransformable.hpp:107
Definition: JacobiRotatable.hpp:50
Definition: JacobiRotation.hpp:33
Eigen::JacobiRotation< double > Eigen() const
Definition: JacobiRotation.hpp:103
size_t p() const
Definition: JacobiRotation.hpp:88
size_t q() const
Definition: JacobiRotation.hpp:93
const MatrixRepresentation & parameters(const Indices &... indices) const
Definition: SQOperatorStorageBase.hpp:236
size_t numberOfOrbitals() const
Definition: SQOperatorStorageBase.hpp:277
const std::vector< MatrixRepresentation > & allParameters() const
Definition: SQOperatorStorageBase.hpp:221
StorageArray< MatrixRepresentation, Vectorizer > array
Definition: SQOperatorStorageBase.hpp:68
size_t numberOfComponents() const
Definition: SQOperatorStorageBase.hpp:272
Definition: SQOperatorStorage.hpp:44
Definition: SimpleSQOneElectronOperator.hpp:43
enable_if_t< std::is_same< Z, double >::value, StorageArray< SquareRankFourTensor< double >, Vectorizer > > calculateSuperFockianMatrix(const Derived1DM &DM, const Derived2DM &dm) const
Definition: SimpleSQOneElectronOperator.hpp:178
typename OperatorTraits< DerivedOperator >::Transformation Transformation
Definition: SimpleSQOneElectronOperator.hpp:64
_Vectorizer Vectorizer
Definition: SimpleSQOneElectronOperator.hpp:49
enable_if_t< std::is_same< Z, double >::value, StorageArray< SquareMatrix< double >, Vectorizer > > calculateFockianMatrix(const Derived1DM &DM, const Derived2DM &dm) const
Definition: SimpleSQOneElectronOperator.hpp:132
typename OperatorTraits< DerivedOperator >::TwoDM Derived2DM
Definition: SimpleSQOneElectronOperator.hpp:70
_DerivedOperator DerivedOperator
Definition: SimpleSQOneElectronOperator.hpp:52
typename OperatorTraits< DerivedOperator >::MullikenDomain MullikenDomain
Definition: SimpleSQOneElectronOperator.hpp:73
DerivedOperator partitioned(const Transformation &mulliken_projection_matrix) const
Definition: SimpleSQOneElectronOperator.hpp:328
DerivedOperator oneIndexTransformed(const Transformation &T) const
Definition: SimpleSQOneElectronOperator.hpp:301
DerivedOperator rotated(const JacobiRotation &jacobi_rotation) const override
Definition: SimpleSQOneElectronOperator.hpp:269
typename OperatorTraits< DerivedOperator >::ScalarOperator ScalarDerivedOperator
Definition: SimpleSQOneElectronOperator.hpp:61
typename OperatorTraits< DerivedOperator >::OneDM Derived1DM
Definition: SimpleSQOneElectronOperator.hpp:67
StorageArray< Scalar, Vectorizer > calculateExpectationValue(const Derived1DM &D) const
Definition: SimpleSQOneElectronOperator.hpp:96
DerivedOperator transformed(const Transformation &T) const override
Definition: SimpleSQOneElectronOperator.hpp:235
_Scalar Scalar
Definition: SimpleSQOneElectronOperator.hpp:46
static Self Zero(const size_t dim)
Definition: SquareMatrix.hpp:289
Definition: SquareRankFourTensor.hpp:36
Definition: StorageArray.hpp:38
const Vectorizer & vectorizer() const
Definition: StorageArray.hpp:177
Definition: Tensor.hpp:46
Definition: BaseOneElectronIntegralBuffer.hpp:25
typename std::enable_if< B, T >::type enable_if_t
Definition: type_traits.hpp:37
_DerivedOperator DerivedOperator
Definition: SimpleSQOneElectronOperator.hpp:349
Definition: OperatorTraits.hpp:28